{"action":"created","comment":{"url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/comments/70366921","id":70366921,"diff_hunk":"@@ -0,0 +1,598 @@\n+# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: t -*-\n+# vi: set ft=python sts=4 ts=4 sw=4 noet :\n+\n+# This file is part of Fail2Ban.\n+#\n+# Fail2Ban is free software; you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation; either version 2 of the License, or\n+# (at your option) any later version.\n+#\n+# Fail2Ban is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with Fail2Ban; if not, write to the Free Software\n+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n+\n+# Fail2Ban developers\n+\n+__author__ = \"Serg Brester\"\n+__copyright__ = \"Copyright (c) 2014- Serg G. Brester (sebres), 2008- Fail2Ban Contributors\"\n+__license__ = \"GPL\"\n+\n+import fileinput\n+import os\n+import re\n+import sys\n+import time\n+import signal\n+import unittest\n+\n+from functools import wraps\n+from threading import Thread\n+\n+\n+from ..client import fail2banclient, fail2banserver, fail2bancmdline\n+from ..client.fail2banclient import Fail2banClient, exec_command_line as _exec_client, VisualWait\n+from ..client.fail2banserver import Fail2banServer, exec_command_line as _exec_server\n+from .. import protocol\n+from ..server import server\n+from ..server.utils import Utils\n+from .utils import LogCaptureTestCase, logSys, with_tmpdir, shutil, logging\n+\n+\n+STOCK_CONF_DIR = \"config\"\n+STOCK = os.path.exists(os.path.join(STOCK_CONF_DIR,'fail2ban.conf'))\n+\n+CLIENT = \"fail2ban-client\"\n+SERVER = \"fail2ban-server\"\n+BIN = os.path.dirname(Fail2banServer.getServerPath())\n+\n+MAX_WAITTIME = 30 if not unittest.F2B.fast else 5\n+\n+##\n+# Several wrappers and settings for proper testing:\n+#\n+\n+fail2bancmdline.MAX_WAITTIME = MAX_WAITTIME-1\n+\n+fail2bancmdline.logSys = \\\n+fail2banclient.logSys = \\\n+fail2banserver.logSys = logSys\n+\n+server.DEF_LOGTARGET = \"/dev/null\"\n+\n+def _test_output(*args):\n+\tlogSys.info(args[0])\n+fail2bancmdline.output = \\\n+fail2banclient.output = \\\n+fail2banserver.output = \\\n+protocol.output = _test_output\n+\n+def _test_exit(code=0):\n+\tlogSys.debug(\"Exit with code %s\", code)\n+\tif code == 0:\n+\t\traise ExitException()\n+\telse:\n+\t\traise FailExitException()   \n+fail2bancmdline.exit = \\\n+fail2banclient.exit = \\\n+fail2banserver.exit = _test_exit\n+\n+INTERACT = []\n+def _test_input_command(*args):\n+\tif len(INTERACT):\n+\t\t#logSys.debug('--- interact command: %r', INTERACT[0])\n+\t\treturn INTERACT.pop(0)\n+\telse:\n+\t\treturn \"exit\" \n+fail2banclient.input_command = _test_input_command\n+\n+# prevents change logging params, log capturing, etc:\n+fail2bancmdline.PRODUCTION = \\\n+fail2banclient.PRODUCTION = \\\n+fail2banserver.PRODUCTION = False\n+\n+\n+class ExitException(fail2bancmdline.ExitException):\n+\tpass\n+class FailExitException(fail2bancmdline.ExitException):\n+\tpass\n+\n+\n+def _out_file(fn): # pragma: no cover\n+\tlogSys.debug('---- ' + fn + ' ----')\n+\tfor line in fileinput.input(fn):\n+\t\tline = line.rstrip('\\n')\n+\t\tlogSys.debug(line)\n+\tlogSys.debug('-'*30)\n+\n+def _start_params(tmp, use_stock=False, logtarget=\"/dev/null\"):\n+\tcfg = tmp+\"/config\"\n+\tif use_stock and STOCK:\n+\t\t# copy config (sub-directories as alias):\n+\t\tdef ig_dirs(dir, files):\n+\t\t\treturn [f for f in files if os.path.isdir(os.path.join(dir, f))]\n+\t\tshutil.copytree(STOCK_CONF_DIR, cfg, ignore=ig_dirs)\n+\t\tos.symlink(STOCK_CONF_DIR+\"/action.d\", cfg+\"/action.d\")\n+\t\tos.symlink(STOCK_CONF_DIR+\"/filter.d\", cfg+\"/filter.d\")\n+\t\t# replace fail2ban params (database with memory):\n+\t\tr = re.compile(r'^dbfile\\s*=')\n+\t\tfor line in fileinput.input(cfg+\"/fail2ban.conf\", inplace=True):\n+\t\t\tline = line.rstrip('\\n')\n+\t\t\tif r.match(line):\n+\t\t\t\tline = \"dbfile = :memory:\"\n+\t\t\tprint(line)\n+\t\t# replace jail params (polling as backend to be fast in initialize):\n+\t\tr = re.compile(r'^backend\\s*=')\n+\t\tfor line in fileinput.input(cfg+\"/jail.conf\", inplace=True):\n+\t\t\tline = line.rstrip('\\n')\n+\t\t\tif r.match(line):\n+\t\t\t\tline = \"backend = polling\"\n+\t\t\tprint(line)\n+\telse:\n+\t\t# just empty config directory without anything (only fail2ban.conf/jail.conf):\n+\t\tos.mkdir(cfg)\n+\t\tf = open(cfg+\"/fail2ban.conf\", \"w\")\n+\t\tf.write('\\n'.join((\n+\t\t\t\"[Definition]\",\n+\t\t\t\"loglevel = INFO\",\n+\t\t\t\"logtarget = \" + logtarget,\n+\t\t\t\"syslogsocket = auto\",\n+\t\t\t\"socket = \"+tmp+\"/f2b.sock\",\n+\t\t\t\"pidfile = \"+tmp+\"/f2b.pid\",\n+\t\t\t\"backend = polling\",\n+\t\t\t\"dbfile = :memory:\",\n+\t\t\t\"dbpurgeage = 1d\",\n+\t\t\t\"\",\n+\t\t)))\n+\t\tf.close()\n+\t\tf = open(cfg+\"/jail.conf\", \"w\")\n+\t\tf.write('\\n'.join((\n+\t\t\t\"[INCLUDES]\", \"\",\n+\t\t\t\"[DEFAULT]\", \"\",\n+\t\t\t\"\",\n+\t\t)))\n+\t\tf.close()\n+\t\tif logSys.level < logging.DEBUG: # if HEAVYDEBUG\n+\t\t\t_out_file(cfg+\"/fail2ban.conf\")\n+\t\t\t_out_file(cfg+\"/jail.conf\")\n+\t# parameters (sock/pid and config, increase verbosity, set log, etc.):\n+\treturn (\"-c\", cfg, \"-s\", tmp+\"/f2b.sock\", \"-p\", tmp+\"/f2b.pid\",\n+\t\t\t\t\t\"-vv\", \"--logtarget\", logtarget, \"--loglevel\", \"DEBUG\", \"--syslogsocket\", \"auto\",\n+\t\t\t\t\t\"--timeout\", str(fail2bancmdline.MAX_WAITTIME),\n+\t)\n+\n+def _kill_srv(pidfile): # pragma: no cover","path":"fail2ban/tests/fail2banclienttestcase.py","position":169,"original_position":169,"commit_id":"6a2b70f11db9841495c384f30e53941714ebd18a","original_commit_id":"06dcad76504f82621a11a5156279d02593538d00","user":{"login":"yarikoptic","id":39889,"avatar_url":"https://avatars.githubusercontent.com/u/39889?v=3","gravatar_id":"","url":"https://api.github.com/users/yarikoptic","html_url":"https://github.com/yarikoptic","followers_url":"https://api.github.com/users/yarikoptic/followers","following_url":"https://api.github.com/users/yarikoptic/following{/other_user}","gists_url":"https://api.github.com/users/yarikoptic/gists{/gist_id}","starred_url":"https://api.github.com/users/yarikoptic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yarikoptic/subscriptions","organizations_url":"https://api.github.com/users/yarikoptic/orgs","repos_url":"https://api.github.com/users/yarikoptic/repos","events_url":"https://api.github.com/users/yarikoptic/events{/privacy}","received_events_url":"https://api.github.com/users/yarikoptic/received_events","type":"User","site_admin":false},"body":"so, should we later investigate failures within the helpers, happen they come to help us whenever main thing goes kaboom?  nope... so they should (theoretically) be tested as well.  Thus -- `no cover` is not warranted.","created_at":"2016-07-12T01:53:15Z","updated_at":"2016-07-12T01:53:15Z","html_url":"https://github.com/fail2ban/fail2ban/pull/1413#discussion_r70366921","pull_request_url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413","_links":{"self":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/comments/70366921"},"html":{"href":"https://github.com/fail2ban/fail2ban/pull/1413#discussion_r70366921"},"pull_request":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413"}}},"pull_request":{"url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413","id":69818436,"html_url":"https://github.com/fail2ban/fail2ban/pull/1413","diff_url":"https://github.com/fail2ban/fail2ban/pull/1413.diff","patch_url":"https://github.com/fail2ban/fail2ban/pull/1413.patch","issue_url":"https://api.github.com/repos/fail2ban/fail2ban/issues/1413","number":1413,"state":"open","locked":false,"title":"0.10-cs: client-server rewritten as modules / start in foreground / test coverage for cs","user":{"login":"sebres","id":3769657,"avatar_url":"https://avatars.githubusercontent.com/u/3769657?v=3","gravatar_id":"","url":"https://api.github.com/users/sebres","html_url":"https://github.com/sebres","followers_url":"https://api.github.com/users/sebres/followers","following_url":"https://api.github.com/users/sebres/following{/other_user}","gists_url":"https://api.github.com/users/sebres/gists{/gist_id}","starred_url":"https://api.github.com/users/sebres/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebres/subscriptions","organizations_url":"https://api.github.com/users/sebres/orgs","repos_url":"https://api.github.com/users/sebres/repos","events_url":"https://api.github.com/users/sebres/events{/privacy}","received_events_url":"https://api.github.com/users/sebres/received_events","type":"User","site_admin":false},"body":"closes #1121\r\ncloses  #1139 \r\nSeveral enhancements, bug fixed and code optimization:\r\n\r\n* starting of the server (and client/server communication behavior during start and daemonize) completely rewritten:\r\n\r\n  - client/server functionality moved away from bin and using now the common interface (introduced in fail2bancmdline);\r\n  - start in foreground fixed;\r\n  - server can act as client corresponding command line;\r\n  - command \"restart\" added: in opposite to \"reload\" in reality restarts the server (new process);\r\n  - several client/server bugs during starting process fixed.\r\n\r\n* possibility to increase verbosity up to heavy debug (command line parameter `-vvv`)\r\n* increase readability and details level by increased verbosity\r\n* reader bug fix: prevent to silent \"load\" of not existing jail;\r\n* coverage of test cases increased;\r\n* several bug fixed: fork in client-server test cases prohibited, all worker threads daemonized (to prevent hanging on exit).\r\n","created_at":"2016-05-12T11:11:27Z","updated_at":"2016-07-12T01:53:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7303714e96b60d4d13f06e9810c82433a6d45996","assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/fail2ban/fail2ban/milestones/14","html_url":"https://github.com/fail2ban/fail2ban/milestones/0.10.0","labels_url":"https://api.github.com/repos/fail2ban/fail2ban/milestones/14/labels","id":1759181,"number":14,"title":"0.10.0","description":"Major functionality and performance boosts: semantic times, IPv6 support etc","creator":{"login":"yarikoptic","id":39889,"avatar_url":"https://avatars.githubusercontent.com/u/39889?v=3","gravatar_id":"","url":"https://api.github.com/users/yarikoptic","html_url":"https://github.com/yarikoptic","followers_url":"https://api.github.com/users/yarikoptic/followers","following_url":"https://api.github.com/users/yarikoptic/following{/other_user}","gists_url":"https://api.github.com/users/yarikoptic/gists{/gist_id}","starred_url":"https://api.github.com/users/yarikoptic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yarikoptic/subscriptions","organizations_url":"https://api.github.com/users/yarikoptic/orgs","repos_url":"https://api.github.com/users/yarikoptic/repos","events_url":"https://api.github.com/users/yarikoptic/events{/privacy}","received_events_url":"https://api.github.com/users/yarikoptic/received_events","type":"User","site_admin":false},"open_issues":4,"closed_issues":14,"state":"open","created_at":"2016-05-11T01:19:51Z","updated_at":"2016-06-12T13:10:38Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413/commits","review_comments_url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413/comments","review_comment_url":"https://api.github.com/repos/fail2ban/fail2ban/pulls/comments{/number}","comments_url":"https://api.github.com/repos/fail2ban/fail2ban/issues/1413/comments","statuses_url":"https://api.github.com/repos/fail2ban/fail2ban/statuses/6a2b70f11db9841495c384f30e53941714ebd18a","head":{"label":"sebres:f2b-perfom-prepare-716-cs-0.10","ref":"f2b-perfom-prepare-716-cs-0.10","sha":"6a2b70f11db9841495c384f30e53941714ebd18a","user":{"login":"sebres","id":3769657,"avatar_url":"https://avatars.githubusercontent.com/u/3769657?v=3","gravatar_id":"","url":"https://api.github.com/users/sebres","html_url":"https://github.com/sebres","followers_url":"https://api.github.com/users/sebres/followers","following_url":"https://api.github.com/users/sebres/following{/other_user}","gists_url":"https://api.github.com/users/sebres/gists{/gist_id}","starred_url":"https://api.github.com/users/sebres/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebres/subscriptions","organizations_url":"https://api.github.com/users/sebres/orgs","repos_url":"https://api.github.com/users/sebres/repos","events_url":"https://api.github.com/users/sebres/events{/privacy}","received_events_url":"https://api.github.com/users/sebres/received_events","type":"User","site_admin":false},"repo":{"id":8566686,"name":"fail2ban","full_name":"sebres/fail2ban","owner":{"login":"sebres","id":3769657,"avatar_url":"https://avatars.githubusercontent.com/u/3769657?v=3","gravatar_id":"","url":"https://api.github.com/users/sebres","html_url":"https://github.com/sebres","followers_url":"https://api.github.com/users/sebres/followers","following_url":"https://api.github.com/users/sebres/following{/other_user}","gists_url":"https://api.github.com/users/sebres/gists{/gist_id}","starred_url":"https://api.github.com/users/sebres/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sebres/subscriptions","organizations_url":"https://api.github.com/users/sebres/orgs","repos_url":"https://api.github.com/users/sebres/repos","events_url":"https://api.github.com/users/sebres/events{/privacy}","received_events_url":"https://api.github.com/users/sebres/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sebres/fail2ban","description":"Daemon to ban hosts that cause multiple authentication errors","fork":true,"url":"https://api.github.com/repos/sebres/fail2ban","forks_url":"https://api.github.com/repos/sebres/fail2ban/forks","keys_url":"https://api.github.com/repos/sebres/fail2ban/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sebres/fail2ban/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sebres/fail2ban/teams","hooks_url":"https://api.github.com/repos/sebres/fail2ban/hooks","issue_events_url":"https://api.github.com/repos/sebres/fail2ban/issues/events{/number}","events_url":"https://api.github.com/repos/sebres/fail2ban/events","assignees_url":"https://api.github.com/repos/sebres/fail2ban/assignees{/user}","branches_url":"https://api.github.com/repos/sebres/fail2ban/branches{/branch}","tags_url":"https://api.github.com/repos/sebres/fail2ban/tags","blobs_url":"https://api.github.com/repos/sebres/fail2ban/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sebres/fail2ban/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sebres/fail2ban/git/refs{/sha}","trees_url":"https://api.github.com/repos/sebres/fail2ban/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sebres/fail2ban/statuses/{sha}","languages_url":"https://api.github.com/repos/sebres/fail2ban/languages","stargazers_url":"https://api.github.com/repos/sebres/fail2ban/stargazers","contributors_url":"https://api.github.com/repos/sebres/fail2ban/contributors","subscribers_url":"https://api.github.com/repos/sebres/fail2ban/subscribers","subscription_url":"https://api.github.com/repos/sebres/fail2ban/subscription","commits_url":"https://api.github.com/repos/sebres/fail2ban/commits{/sha}","git_commits_url":"https://api.github.com/repos/sebres/fail2ban/git/commits{/sha}","comments_url":"https://api.github.com/repos/sebres/fail2ban/comments{/number}","issue_comment_url":"https://api.github.com/repos/sebres/fail2ban/issues/comments{/number}","contents_url":"https://api.github.com/repos/sebres/fail2ban/contents/{+path}","compare_url":"https://api.github.com/repos/sebres/fail2ban/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sebres/fail2ban/merges","archive_url":"https://api.github.com/repos/sebres/fail2ban/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sebres/fail2ban/downloads","issues_url":"https://api.github.com/repos/sebres/fail2ban/issues{/number}","pulls_url":"https://api.github.com/repos/sebres/fail2ban/pulls{/number}","milestones_url":"https://api.github.com/repos/sebres/fail2ban/milestones{/number}","notifications_url":"https://api.github.com/repos/sebres/fail2ban/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sebres/fail2ban/labels{/name}","releases_url":"https://api.github.com/repos/sebres/fail2ban/releases{/id}","deployments_url":"https://api.github.com/repos/sebres/fail2ban/deployments","created_at":"2013-03-04T22:36:47Z","updated_at":"2016-06-23T07:42:56Z","pushed_at":"2016-07-11T11:29:36Z","git_url":"git://github.com/sebres/fail2ban.git","ssh_url":"git@github.com:sebres/fail2ban.git","clone_url":"https://github.com/sebres/fail2ban.git","svn_url":"https://github.com/sebres/fail2ban","homepage":"http://www.fail2ban.org","size":7500,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":7,"default_branch":"0.10-full"}},"base":{"label":"fail2ban:0.10","ref":"0.10","sha":"7582f13003879f55a5e2bbb18eff48341e33e432","user":{"login":"fail2ban","id":1087378,"avatar_url":"https://avatars.githubusercontent.com/u/1087378?v=3","gravatar_id":"","url":"https://api.github.com/users/fail2ban","html_url":"https://github.com/fail2ban","followers_url":"https://api.github.com/users/fail2ban/followers","following_url":"https://api.github.com/users/fail2ban/following{/other_user}","gists_url":"https://api.github.com/users/fail2ban/gists{/gist_id}","starred_url":"https://api.github.com/users/fail2ban/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fail2ban/subscriptions","organizations_url":"https://api.github.com/users/fail2ban/orgs","repos_url":"https://api.github.com/users/fail2ban/repos","events_url":"https://api.github.com/users/fail2ban/events{/privacy}","received_events_url":"https://api.github.com/users/fail2ban/received_events","type":"Organization","site_admin":false},"repo":{"id":2476162,"name":"fail2ban","full_name":"fail2ban/fail2ban","owner":{"login":"fail2ban","id":1087378,"avatar_url":"https://avatars.githubusercontent.com/u/1087378?v=3","gravatar_id":"","url":"https://api.github.com/users/fail2ban","html_url":"https://github.com/fail2ban","followers_url":"https://api.github.com/users/fail2ban/followers","following_url":"https://api.github.com/users/fail2ban/following{/other_user}","gists_url":"https://api.github.com/users/fail2ban/gists{/gist_id}","starred_url":"https://api.github.com/users/fail2ban/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fail2ban/subscriptions","organizations_url":"https://api.github.com/users/fail2ban/orgs","repos_url":"https://api.github.com/users/fail2ban/repos","events_url":"https://api.github.com/users/fail2ban/events{/privacy}","received_events_url":"https://api.github.com/users/fail2ban/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/fail2ban/fail2ban","description":"Daemon to ban hosts that cause multiple authentication errors","fork":false,"url":"https://api.github.com/repos/fail2ban/fail2ban","forks_url":"https://api.github.com/repos/fail2ban/fail2ban/forks","keys_url":"https://api.github.com/repos/fail2ban/fail2ban/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fail2ban/fail2ban/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fail2ban/fail2ban/teams","hooks_url":"https://api.github.com/repos/fail2ban/fail2ban/hooks","issue_events_url":"https://api.github.com/repos/fail2ban/fail2ban/issues/events{/number}","events_url":"https://api.github.com/repos/fail2ban/fail2ban/events","assignees_url":"https://api.github.com/repos/fail2ban/fail2ban/assignees{/user}","branches_url":"https://api.github.com/repos/fail2ban/fail2ban/branches{/branch}","tags_url":"https://api.github.com/repos/fail2ban/fail2ban/tags","blobs_url":"https://api.github.com/repos/fail2ban/fail2ban/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fail2ban/fail2ban/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fail2ban/fail2ban/git/refs{/sha}","trees_url":"https://api.github.com/repos/fail2ban/fail2ban/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fail2ban/fail2ban/statuses/{sha}","languages_url":"https://api.github.com/repos/fail2ban/fail2ban/languages","stargazers_url":"https://api.github.com/repos/fail2ban/fail2ban/stargazers","contributors_url":"https://api.github.com/repos/fail2ban/fail2ban/contributors","subscribers_url":"https://api.github.com/repos/fail2ban/fail2ban/subscribers","subscription_url":"https://api.github.com/repos/fail2ban/fail2ban/subscription","commits_url":"https://api.github.com/repos/fail2ban/fail2ban/commits{/sha}","git_commits_url":"https://api.github.com/repos/fail2ban/fail2ban/git/commits{/sha}","comments_url":"https://api.github.com/repos/fail2ban/fail2ban/comments{/number}","issue_comment_url":"https://api.github.com/repos/fail2ban/fail2ban/issues/comments{/number}","contents_url":"https://api.github.com/repos/fail2ban/fail2ban/contents/{+path}","compare_url":"https://api.github.com/repos/fail2ban/fail2ban/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fail2ban/fail2ban/merges","archive_url":"https://api.github.com/repos/fail2ban/fail2ban/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fail2ban/fail2ban/downloads","issues_url":"https://api.github.com/repos/fail2ban/fail2ban/issues{/number}","pulls_url":"https://api.github.com/repos/fail2ban/fail2ban/pulls{/number}","milestones_url":"https://api.github.com/repos/fail2ban/fail2ban/milestones{/number}","notifications_url":"https://api.github.com/repos/fail2ban/fail2ban/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fail2ban/fail2ban/labels{/name}","releases_url":"https://api.github.com/repos/fail2ban/fail2ban/releases{/id}","deployments_url":"https://api.github.com/repos/fail2ban/fail2ban/deployments","created_at":"2011-09-28T16:24:20Z","updated_at":"2016-07-09T15:16:50Z","pushed_at":"2016-07-11T11:29:37Z","git_url":"git://github.com/fail2ban/fail2ban.git","ssh_url":"git@github.com:fail2ban/fail2ban.git","clone_url":"https://github.com/fail2ban/fail2ban.git","svn_url":"https://github.com/fail2ban/fail2ban","homepage":"http://www.fail2ban.org","size":13424,"stargazers_count":1534,"watchers_count":1534,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":374,"mirror_url":null,"open_issues_count":222,"forks":374,"open_issues":222,"watchers":1534,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413"},"html":{"href":"https://github.com/fail2ban/fail2ban/pull/1413"},"issue":{"href":"https://api.github.com/repos/fail2ban/fail2ban/issues/1413"},"comments":{"href":"https://api.github.com/repos/fail2ban/fail2ban/issues/1413/comments"},"review_comments":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413/comments"},"review_comment":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/fail2ban/fail2ban/pulls/1413/commits"},"statuses":{"href":"https://api.github.com/repos/fail2ban/fail2ban/statuses/6a2b70f11db9841495c384f30e53941714ebd18a"}}}}
{"actor":{"display_login":"yarikoptic"}}