Browse Source

first commit

becivells 5 years ago
commit
110fb7b114

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+Thumbs.db
+ehthumbs.db
+Desktop.ini
+
+# Python:
+*.py[cod]
+*.so
+*.egg
+*.egg-info
+*.log
+dist
+build
+
+#pychram
+.idea
+*.log.*
+
+#data
+conf/git.conf
+conf/git.conf.bak

+ 8 - 0
conf/__init__.py

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : __init__.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/6
+#@Software : PyCharm
+# @Desc  :

+ 137 - 0
conf/confapi.py

@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : confapi.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/6
+#@Software : PyCharm
+# @Desc  :
+import os
+import sys
+import json
+import shutil
+import argparse
+
+BASE_DIR = os.path.dirname(os.path.realpath(__file__))
+conf_path = BASE_DIR + os.sep + 'git.conf'
+
+git_serverip = '123.206.177.125'
+
+def addconf(uid,name,path,ip=None,user='apache',interval=3,cmd='git pull origin master',update=False):
+    if isinstance(ip,list):
+        ip.append(git_serverip)
+        _ip = ','.join('"%s"'%(i) for i in ip)
+    else:
+        _ip = '"{git_serverip}"'.format(git_serverip=git_serverip)
+
+    repo = '''{{
+        "{uid}": {{
+            "name": "{name}",
+            "path": "{path}",
+            "ip": ["127.0.0.1",{ip}],
+            "cmd": "{cmd}",
+            "user": "{user}",
+            "interval": {interval}
+        }}
+    }}'''.format(uid=uid, name=name, path=path,ip=_ip,user=user,cmd=cmd,interval=interval)
+    repos = getconf()
+    if uid in repos and not update:
+       print ('uuid is exist if you want to update  please use --update')
+       print (json.dumps({uid:(repos.get(uid))},indent=4))
+       print ('----------error---------')
+       sys.exit(-1)
+    with open(conf_path,'a') as f:
+        f.write(json.dumps(json.loads(repo)))
+        f.write('\n')
+    print (repo)
+
+
+def getconf():
+    _conf_dict = {}
+    with open(conf_path,'r') as f:
+        for line in f:
+            if line.strip():
+                _conf_dict.update(json.loads(line))
+            else:
+                print ('blank line')
+    return _conf_dict
+
+
+def check():
+    repo_dict = getconf()
+    flag = False
+    shutil.copy(conf_path,conf_path+'.bak')
+    with open(conf_path,'w') as f:
+        for k,v in repo_dict.items():
+            f.write(json.dumps({k:v}))
+            f.write('\n')
+    # print(json.dumps(getconf(),indent=4))
+
+
+def parse_cmd_args():
+    '''处理命令行选项
+
+    '''
+
+    import argparse
+    import uuid
+
+    parser = argparse.ArgumentParser(
+        prog='add gitconf',
+        description='add repo')
+
+    parser.add_argument("--uid", dest='uid', action="store",
+                       help="repo sync uid")
+
+    parser.add_argument("--update", dest='update', action="store",
+                        # default=  str(uuid.uuid1()), # 不能有默认值
+                        default=False,
+                       help="update date")
+
+    parser.add_argument("--cmd", dest='cmd', action="store",
+                        default=  'git pull origin master', # 不能有默认值
+                       help=("please use --cmd \"git pull origin master\" "
+                            "not --cmd 'git pull origin master' .etc "))
+
+
+    parser.add_argument("--name", action="store",
+                       dest='name',
+                       help="repo name")
+
+    parser.add_argument("--path", action="store",
+                       dest='path',
+                       help="repo name")
+
+    parser.add_argument('--ip',
+                        action="store",
+                        nargs='+',
+                        default=git_serverip,
+                        help="allow access ip address")
+
+    parser.add_argument("--user", action="store_true", default='apache',
+                        dest='user',
+                        help="user")
+
+    parser.add_argument('--interval',
+                        action="store",
+                        default='3',
+                        dest='interval',
+                        help='interval default 3s',type=int)
+
+    args = parser.parse_args()
+    if not (args.uid and args.name and args.path):
+        parser.print_help()
+        sys.exit(0)
+
+    return args
+if __name__ == '__main__':
+    args = parse_cmd_args()
+    cmd = args.cmd
+    if args.update:
+        print('update data')
+        addconf(args.uid, args.name, args.path, ip=args.ip, user=args.user,
+                interval=args.interval,cmd=cmd, update=True)
+        check()
+    else:
+        addconf(args.uid, args.name, args.path, ip=args.ip, user=args.user,
+                interval=args.interval,cmd=cmd, update=False)

+ 94 - 0
config.py

@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : config.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/10/19
+#@Software : PyCharm
+# @Desc  :
+
+import os
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+basedir = os.path.abspath(os.path.dirname(__file__))
+dbdir = basedir + os.sep + 'db'
+
+HOST = '0.0.0.0'
+PORT = 21332
+DEBUG = False   #True or False
+
+#允许git server地址访问
+GIT_SERVER = [
+    #localhost
+    '127.0.0.1',
+    '123.206.177.125'
+]
+
+#访问的路径
+SYNC_PATH = 'hookssync'
+
+##如果token不存在路径访问正确那么访问时频繁更新配置间隔的时间
+CONF_UPDATE_TIME = 30
+
+#允许执行命令时的路径
+PATH_WHITE_LIST = [
+    # 匹配所有
+    '.*',
+    # /app/web/test
+    '^/app/web/.*',
+    # /app/web/tests
+    '^/var/www/html/.*',
+    '/www/wwwroot/.*'
+]
+
+SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess'
+
+
+SQLALCHEMY_DATABASE_SQLITE_URI = "mysql+pymysql://root:password@localhost:3306/webhook?charset=utf8"
+    # os.environ.get('DATABASE_URL') or \
+    #     'sqlite:///' + os.path.join(dbdir, 'app.db')
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+engine = create_engine(SQLALCHEMY_DATABASE_SQLITE_URI)
+session_class = sessionmaker(bind=engine)
+
+#允许在webhooks执行时执行的命令 使用正则表达式进行匹配
+EXEC_WHITE_LIST = [
+     # 匹配所有
+     '.*',
+     # git pull origin master
+    '^git pull \w+ \w+$',
+     # git fetch origin master && git reset --hard origin/master
+    '^git fetch \w+ \w+ && git reset --hard \w+/\w+$',
+    # git pull origin master && supervisorctl restart webhooks
+    '^git pull \w+ \w+ && supervisorctl restart \w+$',
+    # git fetch origin master && git reset --hard origin/master && supervisorctl restart webhooks
+    '^git fetch \w+ \w+ && git reset --hard \w+/\w+ && supervisorctl restart \w+$'
+]
+
+
+
+repo = {
+    "9901b6deb47111e7b832704d7b885ead":{
+        "name":"hneccp",
+        "path":"/app/web/www.hneccp.com/",
+        "ip":['127.0.0.1','123.206.177.125'],
+        "cmd":'git pull origin master',
+        "user":'apache',
+        'interval':10
+    },
+}
+
+# repo = {
+#     "9901b6deb47111e7b832704d7b885ead":{
+#         "name":"fanghuishop",
+#         "path":"/app/web/fanghuishop",
+#         "ip":['127.0.0.1','123.206.177.125'],
+#         "cmd":'git fetch origin master && git reset --hard origin/master',
+#         "user":'apache',
+#         'interval':3
+#     }
+# }
+
+
+

+ 73 - 0
config.py.product

@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : config.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/10/19
+#@Software : PyCharm
+# @Desc  :
+
+
+HOST = '0.0.0.0'
+PORT = 5000
+DEBUG = False   #True or False
+
+#允许git server地址访问
+GIT_SERVER = [
+    #localhost
+    '127.0.0.1',
+    '123.206.177.125'
+]
+
+#访问的路径
+SYNC_PATH = 'hookssync'
+
+##如果token不存在路径访问正确那么访问时频繁更新配置间隔的时间
+CONF_UPDATE_TIME = 30
+
+#允许执行命令时的路径
+PATH_WHITE_LIST = [
+    # /app/web/test
+    '^/app/web/.*',
+    # /app/web/tests
+    '^/var/www/html/.*'
+]
+
+#允许在webhooks执行时执行的命令 使用正则表达式进行匹配
+EXEC_WHITE_LIST = [
+     # git pull origin master
+    '^git pull \w+ \w+$',
+     # git fetch origin master && git reset --hard origin/master
+    '^git fetch \w+ \w+ && git reset --hard \w+/\w+$',
+    # git pull origin master && supervisorctl restart webhooks
+    '^git pull \w+ \w+ && supervisorctl restart \w+$',
+    # git fetch origin master && git reset --hard origin/master && supervisorctl restart webhooks
+    '^git fetch \w+ \w+ && git reset --hard \w+/\w+ && supervisorctl restart \w+$'
+]
+
+
+
+repo = {
+    "9901b6deb47111e7b832704d7b885ead":{
+        "name":"shop",
+        "path":"/app/web/game",
+        "ip":['127.0.0.1','123.206.177.125'],
+        "cmd":'git pull origin master',
+        "user":'apache',
+        'interval':3
+    }
+}
+
+# repo = {
+#     "9901b6deb47111e7b832704d7b885ead":{
+#         "name":"fanghuishop",
+#         "path":"/app/web/fanghuishop",
+#         "ip":['127.0.0.1','123.206.177.125'],
+#         "cmd":'git fetch origin master && git reset --hard origin/master',
+#         "user":'apache',
+#         'interval':3
+#     }
+# }
+
+
+

+ 73 - 0
core.py

@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : core.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/10/19
+#@Software : PyCharm
+# @Desc  :
+
+import re
+import tempfile
+import subprocess
+from config import repo
+from config import EXEC_WHITE_LIST
+from config import PATH_WHITE_LIST
+from conf.confapi import getconf
+from db.db import get_dbconf
+def runcmd(cmd,path):
+    out_temp = tempfile.SpooledTemporaryFile(max_size=10 * 1000)
+    try:
+        fileno = out_temp.fileno()
+        p = subprocess.Popen(cmd, shell=True,cwd=path, stdout=fileno, stderr=fileno)
+        p.wait()
+        out_temp.seek(0)
+        return out_temp.read()
+    except Exception as e:
+        return str(e)
+    finally:
+        if out_temp:
+            out_temp.close()
+
+def allow_exec(cmd):
+    '''
+    白名单函数允许指定命令运行并且限制长度
+    :param cmd:
+    :return:
+    '''
+    cmd = ' '.join(cmd.split()) #移除多余空格
+
+    if len(cmd) > 100: #限制长度
+        return False
+
+    for whitecmd in EXEC_WHITE_LIST:
+        result = re.findall(whitecmd,cmd)
+
+        if result and cmd == result[0]:
+            return True
+
+    return False
+
+def allow_path(path):
+    '''
+    限制路径
+    :param path:
+    :return:
+    '''
+    path = path.strip()
+    for whitepath in PATH_WHITE_LIST:
+        result = re.findall(whitepath,path)
+
+        if result and path == result[0]:
+            return True
+
+    return False
+
+def getrepo():
+    '''
+    更新配置文件
+    :return:
+    '''
+    repo.update(getconf())
+    repo.update(get_dbconf())
+    return repo

+ 8 - 0
db/__init__.py

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : __init__.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2019/3/11
+#@Software : PyCharm
+# @Desc  :

BIN
db/app.db


+ 70 - 0
db/data.sql

@@ -0,0 +1,70 @@
+-- --------------------------------------------------------
+-- 主机:                           localhost
+-- 服务器版本:                        10.3.8-MariaDB - mariadb.org binary distribution
+-- 服务器操作系统:                      Win64
+-- HeidiSQL 版本:                  9.5.0.5196
+-- --------------------------------------------------------
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET NAMES utf8 */;
+/*!50503 SET NAMES utf8mb4 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+
+-- 导出  表 webhook.data 结构
+CREATE TABLE IF NOT EXISTS `data` (
+  `id` varchar(40) NOT NULL,
+  `name` varchar(30) DEFAULT NULL,
+  `path` varchar(100) DEFAULT NULL,
+  `ip` varchar(200) DEFAULT NULL,
+  `cmd` varchar(100) DEFAULT NULL,
+  `user` varchar(30) DEFAULT NULL,
+  `interval` varchar(30) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+-- 正在导出表  webhook.data 的数据:~0 rows (大约)
+/*!40000 ALTER TABLE `data` DISABLE KEYS */;
+INSERT INTO `data` (`id`, `name`, `path`, `ip`, `cmd`, `user`, `interval`) VALUES
+	('09118e1c-bff0-11e8-8319-704d7b885ead', 'webtes2', '/www/wwwroot/webtes2.web.ximengnaikang.com2', '', 'git pull origin master', 'www', '3'),
+	('18730030-57e4-11e8-a173-704d7b885ead', 'university', '/www/wwwroot/university.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('238f214f-52ae-11e8-803f-704d7b885ead', 'xmnk_api', '/www/wwwroot/api.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('29f33c36-3fd5-11e9-ada6-e84e062b86ac', 'kabao', '/www/wwwroot/kabao.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('36be7770-80b8-11e8-afae-704d7b885ead', 'cangku', '/www/wwwroot/cangku.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('381f0b30-8742-11e8-a0b8-704d7b885ead', 'new_code1', '/www/wwwroot/code1.hnhemeile.com', '', 'git pull origin master', 'www', '3'),
+	('38daa500-55a8-11e8-acd6-704d7b885ead', 'zrjy', '/www/wwwroot/zrjy.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('3d93b091-5013-11e8-aa3e-704d7b885ead', 'wxcdc', '/www/wwwroot/wxcdc.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('3dd5d302-842b-11e8-9be2-525400c45bd1', 'tttest', '/www/wwwroot/tttest.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('4707fbcf-a055-11e8-be22-704d7b885ead', 'wecenter', '/www/wwwroot/wecenter.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('4832a040-8af8-11e8-95ec-704d7b885ead', 'yinglun', '/www/wwwroot/yinglun.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('4c08d8c0-63ec-11e8-86d8-704d7b885ead', 'caidan', '/www/wwwroot/caidan.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('5017ddf0-baf7-11e8-bfc2-704d7b885ead', 'dmglbgs', '/www/wwwroot/dmglbgs.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('53505f30-bff0-11e8-9928-704d7b885ead', 'webtest2', '/www/wwwroot/webtest2.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('55941800-b0b8-11e8-a3a0-704d7b885ead', 'xgzx', '/www/wwwroot/xgzx.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('5b60df0a-e31e-11e8-aeb5-8505d7f1bbe2', 'hycloud', '/www/wwwroot/hycloud.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('5b855112-c2ea-11e8-b5dc-704d7b885ead', 'yktang', '/www/wwwroot/yktang.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('6311d221-60cb-11e8-a870-704d7b885ead', 'qinshang', '/www/wwwroot/qinshang.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('6f5395e1-b0b8-11e8-92d7-704d7b885ead', 'xgzxhtml', '/www/wwwroot/xgzxhtml.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('721f0e5a-179a-11e9-8235-2d0c3576eef0', 'tiexin', '/www/wwwroot/tiexin.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('7e0eba30-a283-11e8-8780-704d7b885ead', 'yanbaoshow', '/www/wwwroot/yanbaoshow.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('86247e28-bfef-11e8-a089-704d7b885ead', 'webtes', '/www/wwwroot/webtes.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('8b30188f-6f97-11e8-b401-704d7b885ead', 'dnflite', '/www/wwwroot/dnflite.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('96ce5de1-a6ad-11e8-a049-704d7b885ead', 'qlc', '/www/wwwroot/qlc.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('979245a1-9700-11e8-9479-704d7b885ead', 'hsjx', '/www/wwwroot/hsjx.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('9e2d00de-8fa7-11e8-af60-704d7b885ead', 'tqx', '/www/wwwroot/tqx.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('a4af51cf-8740-11e8-b05f-704d7b885ead', 'new_code', '/www/wwwroot/code.hnhemeile.com', '', 'git pull origin master', 'www', '3'),
+	('a512fec0-4d2e-11e8-a7e5-704d7b885ead', 'bbbbtest', '/www/wwwroot/bbbbtest.xiimengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('bfe99e70-4ddd-11e8-87f9-704d7b885ead', 'tp_32_test', '/www/wwwroot/tp_32_test.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('c5135900-4de1-11e8-8afc-704d7b885ead', 'html_test', '/www/wwwroot/html_test.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('c87e8b7e-02b7-11e9-9884-b148c48e9157', 'hyygw', '/www/wwwroot/hyygw.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('d63b11c0-5019-11e8-8fb4-704d7b885ead', 'yanbao', '/www/wwwroot/yanbao.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('de92d4d0-cc78-11e8-b21f-6f43d347490b', 'qianyi', '/www/wwwroot/qianyi.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('e3640596-02b6-11e9-bd5e-777287fdfd50', 'hyhtml', '/www/wwwroot/hyhtml.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('eb928480-4dfe-11e8-a058-704d7b885ead', 'html1_test', '/www/wwwroot/html1_test.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('ee5c0d30-4f9d-11e8-95a7-704d7b885ead', 'hhcc', '/www/wwwroot/hhcc.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3'),
+	('xxdedee-seefexdce-esfsfe', 'bbbtest', '/www/wwwroot/bbbtest.web.ximengnaikang.com', '', 'git pull origin master', 'www', '3');
+/*!40000 ALTER TABLE `data` ENABLE KEYS */;
+
+/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
+/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

+ 60 - 0
db/db.py

@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : db.py.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2018/4/21
+#@Software : PyCharm
+# @Desc  :
+import os
+from sqlalchemy import Column,VARCHAR
+from sqlalchemy.ext.declarative import declarative_base
+from config import engine,session_class
+from config import repo as configrepo
+
+Base = declarative_base()
+class Data(Base):
+
+    __tablename__ = 'data'
+
+    id = Column(VARCHAR(40),primary_key=True,nullable=False)
+    name = Column(VARCHAR(30))
+    path = Column(VARCHAR(100))
+    ip = Column(VARCHAR(200))
+    cmd = Column(VARCHAR(100))
+    user = Column(VARCHAR(30))
+    interval = Column(VARCHAR(30))
+
+
+def get_dbconf():
+    session = session_class()
+    datas = session.query(Data).all()
+    session.close()
+    repo = {}
+    for data in datas:
+        if data:
+            if data.ip:
+               ip = data.ip.split(',')
+            else:
+                ip = ''
+            repo.update({data.id:{
+                    "name":data.name,
+                    "path":data.path,
+                    "ip":ip,
+                    "cmd":'git pull origin master',
+                    "user":data.user,
+                    'interval':3
+                    }})
+
+
+    return  repo
+
+
+
+
+# def init():
+#     Base.metadata.create_all(engine)
+#     print (get_dbconf())
+
+
+

+ 39 - 0
db/hooks/post-receive

@@ -0,0 +1,39 @@
+#!/bin/bash
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+syncip=china  #web
+
+port=/app/
+syncname=hookssync
+
+token=sdddddddfsdfeewfwef
+
+#echo color_text
+Color_Text()
+{
+  echo -e " \e[0;$2m$1\e[0m"
+}
+
+Echo_Red()
+{
+  echo $(Color_Text "$1" "31")
+}
+
+Echo_Green()
+{
+  echo $(Color_Text "$1" "32")
+}
+
+Echo_Green 'the code is sync... please wait...'
+Echo_Green '---------------------------sync------------------------------'
+echo ''
+echo ''
+echo ''
+echo ''
+curl -s http://$syncip:$port/$syncname/$token
+echo ''
+Echo_Green '---------------------------sync------------------------------'
+Echo_Green 'Finsh.....'
+```

+ 33 - 0
doc/READ.md

@@ -0,0 +1,33 @@
+
+```
+[root@img hooks]# cat post-receive
+#!/bin/bash
+
+
+curl http://47.52.150.74:5000/hookssync/9901b6deb47111e7b832704d7b885ead
+
+
+chmod 755 -R
+```
+
+
+权限问题导致的版本库冲突 忽略版本权限问题
+$ git config core.filemode false  // 当前版本库
+$ git config --global core.fileMode false // 所有版本库
+
+dev
+工作流程
+webhook部署到 生产服务器中
+配置同步方式
+python confapi.py --uid=仓库识别码 --name=仓库名字  --path=需要代码路径 --ip 有权限进行访问的IP,设置为git服务器的IP
+
+访问
+http://47.52.150.74:5000/hookssync/uid
+等待一段时间后可以同步
+
+git rm --cache
+
+强制合并
+
+git fetch origin master
+git reset --hard origin/master

+ 62 - 0
doc/gitolite/fabfile.py

@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : fabfile.py.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/5
+#@Software : PyCharm
+# @Desc  :
+
+import string
+from random import sample
+from fabric.api import settings
+from fabric.api import cd,run,env,put
+
+env.hosts = ['192.168.233.135']
+env.user='root'
+env.password = 'passwd'
+
+def get_passwd(n=18):
+    abstr = string.digits + string.ascii_letters + '#@$%^&*+_-?<>'
+    return ''.join(sample(abstr,n))
+
+def centos_gitolite_install(user='git',gitdir='/home/'):
+    '''
+    :param user:  git user
+    :param gitdir:  gitdir
+    :return:
+    '''
+
+    githome = '{gitdir}/{user}'.format(gitdir=gitdir, user=user)
+    gitolitehome = '{githome}/gitolite'.format(githome=githome)
+
+    run('mkdir -p {gitdir}'.format(gitdir=gitdir))
+
+    password = get_passwd()
+
+    #init
+    run('yum update -y && yum install epel-release && yum update')
+    run('yum install -y perl openssh git openssh-server')
+    run("yum install 'perl(Data::Dumper)'")
+    run("systemctl enable sshd")
+
+    with settings(warn_only=True):
+        run('adduser {user} -d {githome}'.format(user=user, githome=githome))
+        run('echo "{user}:{password}" | chpasswd'.format(user=user, password=password))
+        print('{user} passwrod: {passwd}'.format(user=user, passwd=password))
+
+    with settings(warn_only=True):
+        result = run('su - {user} -c "git clone git://github.com/sitaramc/gitolite '
+            '{gitolitehome}"'.format(user=user,gitolitehome=gitolitehome))
+        if result.failed:
+            with cd(gitolitehome):
+                run('su {user} -c "git pull"'.format(user=user))
+
+
+    run('su - {user} -c "mkdir -p ~/bin"'.format(user=user))
+    run('su - {user} -c "gitolite/install -to ~/bin"'.format(user=user))
+    with cd('/tmp/'):
+        upload = put('id_rsa.pub','admin.pub')
+        run('su - {user} -c "~/bin/gitolite setup -pk /tmp/admin.pub"'.format(user=user))
+
+

+ 118 - 0
doc/gitolite/git服务器搭建.md

@@ -0,0 +1,118 @@
+##简介
+
+git 版本管理有很多。有gitolite、gitblit、gitlab。gitlab太大而且我们不需要这么多功能。而且不需要web。
+
+发现Gitblit可以很方便的搭建服务,是用Java编写的,带有web管理界面。
+
+gitolite。使用perl写的相当于简介控制了ssh登录操作。登录必须使用证书登录。根据证书识别用户身份
+
+这里我们采用gitolite
+
+##安装
+
+```
+yum install perl openssh git yum install  'perl(Data::Dumper)'
+```
+
+
+
+```
+创建git用户
+adduser  git
+passwd git
+```
+
+
+
+```shell
+切换到git用户
+su - git
+#获取gitolite
+git clone git://github.com/sitaramc/gitolite
+# 创建bin目录,用于存放安装后的文件
+mkdir -p ~/bin
+# 将gitolite安装到bin目录
+gitolite/install -to ~/bin
+# 使用YourName.pub公钥初始化版本库
+gitolite setup -pk YourName.pub
+```
+
+在客户端克隆Gitlite管理库
+
+git clone git@ip:gitolite-admin.git
+
+你可以看到在管理库里,有两个目录, conf/和keydir/,其中conf/下面有个名为gitolite.conf的配置文件。
+
+> - conf/gitolite.conf 用于Git项目配置,访问权限设置。
+> - keydir/ 用于存储用户的SSH public key(公钥)。
+
+
+
+### 增加新用户
+
+增加新用户,就是允许新用户能够通过其公钥访问 Git 服务。只要将新用户的公钥添加到 gitolite-admin 版本库的 keydir 目录下,即完成新用户的添加。管理员从用户获取公钥,并将公钥按照 username.pub 格式进行重命名,然后进入 gitolite-admin 本地克隆版本库中,复制新用户公钥到 keydir 目录,更新到远程版本库即可。
+
+```
+git add.
+git commit -m "add user XXX"
+git push origin master
+```
+
+### 增加版本库
+
+在`conf/gitolite.conf`文件中添加两行,然后更新到远程版本库即可。
+
+```
+repo gitolite-admin
+    RW+                 = jiangxin
+```
+
+### 权限配置
+
+下面我们看一个不那么简单的授权文件:
+
+```
+1   @admin = admin wangsheng
+2
+3   repo gitolite-admin
+4       RW+                 = admin
+5
+6   repo ossxp/.+
+7       C                   = @admin
+8       RW                  = @all
+9
+10  repo testing
+11      RW+                         =   @admin
+12      RW      master              =   junio
+13      RW+     pu                  =   junio
+14      RW      cogito$             =   pasky
+15      RW      bw/                 =   linus
+16      -                           =   somebody
+17      RW      tmp/                =   @all
+18      RW      refs/tags/v[0-9]    =   junio
+```
+
+在上面的示例中,我们演示了很多授权指令。
+
+- 第1行,定义了用户组 @admin,包含两个用户 admin和 wangsheng。
+- 第3-4行,定义了版本库 gitolite-admin。并指定只有用户 admin 才能够访问,并拥有读(R)写(W)和强制更新(+)的权限。
+- 第6行,通过正则表达式定义了一组版本库,即在 ossxp/ 目录下的所有版本库。
+- 第7行,用户组 @admin 中的用户,可以在 ossxp/ 目录下创建版本库。创建版本库的用户,具有对版本库操作的所有权限。
+- 第8行,所有用户都可以读写 ossxp 目录下的版本库,但不能强制更新。
+- 第9行开始,定义的 testing 版本库授权使用了引用授权语法。
+- 第11行,用户组 @admin 对所有的分支和里程碑拥有读写、重置、添加和删除的授权。
+- 第12行,用户 junio 可以读写 master 分支。(还包括名字以 master 开头的其他分支,如果有的话)。
+- 第13行,用户 junio 可以读写、强制更新、创建以及删除 pu 开头的分支。
+- 第14行,用户 pasky 可以读写 cogito 分支。 (仅此分支,精确匹配)。
+
+详细的权限设置请查阅[官方文档](https://github.com/sitaramc/gitolite)。
+
+post-receive
+
+```
+#!/bin/bash
+
+unset  GIT_DIR
+/usr/bin/git --work-tree=/tmp/pyweb --git-dir=/home/git/repositories/pyweb.git checkout -f
+```
+可以使用fab安装

+ 1 - 0
doc/gitolite/id_rsa.pub

@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0JFhoEgrl5/51UHlIIlfWwhiJLR/EEeF8enGnY0PnAldLQ8STdWD8Um2BUtVjgE9COl1X3mN4vMvnSm8A6aPn66enHn0hKzwh1GvcuZNTPgeaZyGKWG0kcvbziUjAPsxxvRvvoaUspSkRYAP/9vpq3SImJKuIHCPfjnGMurKV1n7W/QfpmNjUEwYOswDjL1Ik6Jy6Lrzf8T0hQEy+dYoV4zNM0HcROCXFuu1LyG+WTch3FW660BecNT8+c4sVTHuUMXgGot8OUtwgfXrt5ZL5as7cuyKiWsLVrtrtvL3T0ZHlV8qxQ3DT1gqjSw6jBOzyDOx3jwthAbdsWjyK4Oqp root@user-pc

+ 118 - 0
doc/git服务器搭建.md

@@ -0,0 +1,118 @@
+##简介
+
+git 版本管理有很多。有gitolite、gitblit、gitlab。gitlab太大而且我们不需要这么多功能。而且不需要web。
+
+发现Gitblit可以很方便的搭建服务,是用Java编写的,带有web管理界面。
+
+gitolite。使用perl写的相当于简介控制了ssh登录操作。登录必须使用证书登录。根据证书识别用户身份
+
+这里我们采用gitolite
+
+##安装
+
+```
+yum install perl openssh git yum install  'perl(Data::Dumper)'
+```
+
+
+
+```
+创建git用户
+adduser  git
+passwd git
+```
+
+
+
+```shell
+切换到git用户
+su - git
+#获取gitolite
+git clone git://github.com/sitaramc/gitolite
+# 创建bin目录,用于存放安装后的文件
+mkdir -p ~/bin
+# 将gitolite安装到bin目录
+gitolite/install -to ~/bin
+# 使用YourName.pub公钥初始化版本库
+gitolite setup -pk YourName.pub
+```
+
+在客户端克隆Gitlite管理库
+
+git clone git@ip:gitolite-admin.git
+
+你可以看到在管理库里,有两个目录, conf/和keydir/,其中conf/下面有个名为gitolite.conf的配置文件。
+
+> - conf/gitolite.conf 用于Git项目配置,访问权限设置。
+> - keydir/ 用于存储用户的SSH public key(公钥)。
+
+
+
+### 增加新用户
+
+增加新用户,就是允许新用户能够通过其公钥访问 Git 服务。只要将新用户的公钥添加到 gitolite-admin 版本库的 keydir 目录下,即完成新用户的添加。管理员从用户获取公钥,并将公钥按照 username.pub 格式进行重命名,然后进入 gitolite-admin 本地克隆版本库中,复制新用户公钥到 keydir 目录,更新到远程版本库即可。
+
+```
+git add.
+git commit -m "add user XXX"
+git push origin master
+```
+
+### 增加版本库
+
+在`conf/gitolite.conf`文件中添加两行,然后更新到远程版本库即可。
+
+```
+repo gitolite-admin
+    RW+                 = jiangxin
+```
+
+### 权限配置
+
+下面我们看一个不那么简单的授权文件:
+
+```
+1   @admin = admin wangsheng
+2
+3   repo gitolite-admin
+4       RW+                 = admin
+5
+6   repo ossxp/.+
+7       C                   = @admin
+8       RW                  = @all
+9
+10  repo testing
+11      RW+                         =   @admin
+12      RW      master              =   junio
+13      RW+     pu                  =   junio
+14      RW      cogito$             =   pasky
+15      RW      bw/                 =   linus
+16      -                           =   somebody
+17      RW      tmp/                =   @all
+18      RW      refs/tags/v[0-9]    =   junio
+```
+
+在上面的示例中,我们演示了很多授权指令。
+
+- 第1行,定义了用户组 @admin,包含两个用户 admin和 wangsheng。
+- 第3-4行,定义了版本库 gitolite-admin。并指定只有用户 admin 才能够访问,并拥有读(R)写(W)和强制更新(+)的权限。
+- 第6行,通过正则表达式定义了一组版本库,即在 ossxp/ 目录下的所有版本库。
+- 第7行,用户组 @admin 中的用户,可以在 ossxp/ 目录下创建版本库。创建版本库的用户,具有对版本库操作的所有权限。
+- 第8行,所有用户都可以读写 ossxp 目录下的版本库,但不能强制更新。
+- 第9行开始,定义的 testing 版本库授权使用了引用授权语法。
+- 第11行,用户组 @admin 对所有的分支和里程碑拥有读写、重置、添加和删除的授权。
+- 第12行,用户 junio 可以读写 master 分支。(还包括名字以 master 开头的其他分支,如果有的话)。
+- 第13行,用户 junio 可以读写、强制更新、创建以及删除 pu 开头的分支。
+- 第14行,用户 pasky 可以读写 cogito 分支。 (仅此分支,精确匹配)。
+
+详细的权限设置请查阅[官方文档](https://github.com/sitaramc/gitolite)。
+
+post-receive
+
+```
+#!/bin/bash
+
+unset  GIT_DIR
+/usr/bin/git --work-tree=/tmp/pyweb --git-dir=/home/git/repositories/pyweb.git checkout -f
+```
+

+ 39 - 0
doc/post-receive

@@ -0,0 +1,39 @@
+#!/bin/bash
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+
+#syncip=123.206.177.125  #img
+syncip=123.206.116.149  #web
+
+port=21332
+syncname=hookssync
+
+token=924f6c2e-e13e-11e7-8cc9-e84e062b86ac
+
+#echo color_text
+Color_Text()
+{
+  echo -e " \e[0;$2m$1\e[0m"
+}
+
+Echo_Red()
+{
+  echo $(Color_Text "$1" "31")
+}
+
+Echo_Green()
+{
+  echo $(Color_Text "$1" "32")
+}
+
+Echo_Green 'the code is sync... please wait...'
+Echo_Green '---------------------------sync------------------------------'
+echo ''
+echo ''
+echo ''
+echo ''
+curl -s http://$syncip:$port/$syncname/$token
+echo ''
+Echo_Green '---------------------------sync------------------------------'
+Echo_Green 'Finsh.....'

+ 8 - 0
install/config/__init__.py

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : __init__.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/7
+#@Software : PyCharm
+# @Desc  :

+ 2 - 0
install/config/pip.conf

@@ -0,0 +1,2 @@
+[global]
+index-url = https://pypi.mirrors.ustc.edu.cn/simple

+ 11 - 0
install/config/supervisor_gunicorn.conf.bak

@@ -0,0 +1,11 @@
+[program:gunicorn]
+command=gunicorn -w1 -b0.0.0.0:21332 --error-logfile /opt/webhooks/log/error.log --access-logfile  /opt/webhooks/log/access.log webhooks:app ;应用入口
+user=webhooks
+directory=/opt/webhooks/                         ;web目录
+startsecs=0                                      ;启动时间                                     
+stopwaitsecs=0                                   ;终止等待时间                                     
+autostart=true                                   ;是否自动启动                                   
+autorestart=true                                 ;是否自动重启
+redirect_stderr=true                             ;错误日志输出到标准日志
+stdout_logfile=/dev/null                      ;标准日志不输出
+stdout_logfile_maxbytes=0                        ;标准日志大小

+ 13 - 0
install/config/supervisor_webhooks.conf

@@ -0,0 +1,13 @@
+[program:webhooks]
+command=gunicorn -w1 -b0.0.0.0:21332 --error-logfile /home/flask/webhooks/log/error.log --access-logfile  /home/flask/webhooks/log/access.log webhooks:app ;应用入口
+user=root
+directory=/home/flask/webhooks                         ;web目录
+startsecs=5                                      ;启动时间                                     
+stopwaitsecs=0                                  ;终止等待时间                                     
+startretries = 3 
+autostart=true                                   ;是否自动启动                                   
+autorestart=true                                 ;是否自动重启
+redirect_stderr=true                             ;错误日志输出到标准日志
+stdout_logfile=/dev/null                      ;标准日志不输出
+stdout_logfile_maxbytes=10MB                        ;标准日志大小
+environment=PYTHONPATH=$PYTHONPATH:/home/flask/webhooks

+ 21 - 0
install/config/supervisord.conf

@@ -0,0 +1,21 @@
+[unix_http_server]
+file=/var/run/supervisor/supervisor.sock   
+
+[supervisord]
+nodaemon=false
+logfile=/var/log/supervisor/supervisord.log 
+logfile_maxbytes=50MB
+logfile_backups=10
+loglevel=info
+pidfile=/var/run/supervisord.pid
+
+[supervisorctl]
+serverurl=unix:///var/run/supervisor/supervisor.sock 
+
+[rpcinterface:supervisor]
+supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 
+
+[include]
+files = supervisord.d/*.conf
+
+

+ 68 - 0
install/fabfile.py

@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : fabfile.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/6
+#@Software : PyCharm
+# @Desc  :
+
+from fabric.api import settings
+from fabric.api import cd,run,env,put
+
+from install_config import *
+
+env.hosts = ['192.168.233.136']
+env.user='root'
+env.password = 'password'
+
+
+def pip_mirror():
+    run(("mkdir -p  ~/.pip/ "))
+    with cd('~/.pip/'):
+        put(LOCAL_PIP_CONF_PATH,'~/.pip/')
+    run('pip install -r %s' % (REMOTE_PIP_REQUIREMENT_PATH))
+
+
+def supervisor_install():
+    run('yum install supervisor -y')
+    run('mkdir -p /etc/supervisord.d/')
+    put(LOCAL_SUPERVISORD_CONF_PATH,'/etc/')
+    run('systemctl enable supervisord')
+    run('systemctl start supervisord')
+
+def init():
+    run('yum update -y && yum install epel-release && yum update -y')
+    run('yum install -y python-pip')
+    run('yum install -y git')
+
+def create_user():
+    with settings(warn_only=True):
+        run('useradd -s /usr/sbin/nologin -d {APP_USER_PATH} {APP_USER}'.format(
+            APP_USER=APP_USER,
+            APP_USER_PATH=APP_USER_PATH)
+        )
+
+def upload_web():
+    run('mkdir -p %s'%(APP_PATH))
+    with cd(APP_PATH):
+        put('%s/*'%(LOCAL_BASE_DIR),APP_PATH,mode=777)
+
+    run('chown {APP_USER}:{APP_USER} -R {APP_PATH}'.format(
+        APP_PATH=APP_PATH,
+        APP_USER=APP_USER)
+    )
+
+def web_install():
+    init()
+    supervisor_install()
+    create_user()
+    upload_web()
+    pip_mirror()
+    # get_config()
+    put(LOCAL_GUNICORN_CONF_PATH, '/etc/supervisord.d/')
+    run('systemctl daemon-reload')
+    run('systemctl restart supervisord')
+    run('supervisorctl reload')
+    run('supervisorctl status')
+

+ 84 - 0
install/install_config.py

@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : config.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/12/7
+#@Software : PyCharm
+# @Desc  :
+import os
+
+
+
+
+APP_NAME = 'webhooks'
+APP_INDEX =  'webhooks.py'
+APP_PATH_NAME = 'webhooks'
+APP_PORT = 21332
+APP_WORK = 1
+
+
+APP_USER = 'flask'
+RUN_USER = 'root'
+APP_USER_PATH = '/home/flask'
+#定义的flask app=FLAKSK()
+APP_DEFINE='app'
+
+APP_PATH = APP_USER_PATH + '/' + APP_PATH_NAME
+
+APP_LOG = APP_PATH + '/' +'log'
+
+
+#PROJECT DIR
+LOCAL_BASE_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+#CONF PATH
+LOCAL_CONF_PATH = LOCAL_BASE_DIR + os.sep + 'install' + os.sep + 'config'
+
+LOCAL_SUPERVISORD_CONF_PATH = LOCAL_CONF_PATH + os.sep + 'supervisord.conf'
+
+LOCAL_GUNICORN_CONF_PATH = LOCAL_CONF_PATH + os.sep + 'supervisor_%s.conf'%(APP_NAME)
+
+LOCAL_PIP_CONF_PATH = LOCAL_CONF_PATH + os.sep + 'pip.conf'
+
+
+
+REMOTE_PIP_REQUIREMENT_PATH = APP_USER_PATH + '/' + APP_PATH_NAME + '/' + 'requirements.txt'
+
+
+gunicorn_conf = '''[program:{APP_NAME}]
+command=gunicorn -w{APP_WORK} -b0.0.0.0:{APP_PORT} --error-logfile {APP_LOG}/error.log --access-logfile  {APP_LOG}/access.log {APP_INDEX}:{APP_DEFINE} ;应用入口
+user={RUN_USER}
+directory={APP_PATH}                         ;web目录
+startsecs=5                                      ;启动时间                                     
+stopwaitsecs=0                                  ;终止等待时间                                     
+startretries = 3 
+autostart=true                                   ;是否自动启动                                   
+autorestart=true                                 ;是否自动重启
+redirect_stderr=true                             ;错误日志输出到标准日志
+stdout_logfile=/dev/null                      ;标准日志不输出
+stdout_logfile_maxbytes=10MB                        ;标准日志大小
+environment=PYTHONPATH=$PYTHONPATH:{APP_PATH}
+'''
+
+def check():
+    flask_index_name = '.'.join(APP_INDEX.split('.')[:-1])
+    with open(LOCAL_GUNICORN_CONF_PATH,'w') as f:
+        f.write(gunicorn_conf.format(
+            APP_PATH = APP_PATH,
+            APP_PORT = APP_PORT,
+            APP_NAME = APP_NAME,
+            APP_USER = APP_USER,
+            APP_WORK = APP_WORK,
+            APP_LOG = APP_LOG,
+            RUN_USER = RUN_USER,
+            APP_DEFINE = APP_DEFINE,
+            APP_INDEX = flask_index_name))
+    if not os.path.exists(REMOTE_PIP_REQUIREMENT_PATH):
+        print(u'please create file requirements.txt in %s dir '%(LOCAL_BASE_DIR))
+    if not os.path.exists(LOCAL_CONF_PATH):
+        print('please  install dir error ')
+    if not os.path.exists(LOCAL_PIP_CONF_PATH):
+        print ('pip.conf file not find')
+
+check()

+ 4 - 0
requirements.txt

@@ -0,0 +1,4 @@
+flask
+gunicorn
+pymysql
+sqlalchemy

+ 108 - 0
webhooks.py

@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# @File  : webhooks.py.py
+# @Author: becivells
+#@Contact : becivells@gmail.com
+# @Date  : 2017/10/19
+#@Software : PyCharm
+# @Desc  :
+
+import time
+from flask import Flask
+from flask import request
+
+from core import runcmd
+from core import allow_exec
+from core import allow_path
+from core import getrepo
+from config import HOST
+from config import PORT
+from config import DEBUG
+from config import SYNC_PATH
+from config import GIT_SERVER
+from config import CONF_UPDATE_TIME
+
+app = Flask(__name__)
+
+# app.config.update(dict(
+#     DEBUG=True,
+#     SECRET_KEY='development key'
+# ))
+# app.config.from_envvar('FLASKR_SETTINGS', silent=True)
+
+#记录git仓库更新间隔时间初始状态状态
+status = {}
+
+#初始化配置
+repo = getrepo()
+
+def gettime():
+    return time.time()
+
+#记录如果token不存在ip地址和路径需要更新时初始时间
+confsync = gettime()
+
+@app.route('/')
+def hello_world():
+    return 'Hello World!\n',403
+
+
+@app.route('/{SYNC_PATH}/<token>'.format(SYNC_PATH=SYNC_PATH))
+def sync(token):
+    ip = request.remote_addr.strip()
+    global repo
+    if token not in repo:
+        if gettime() - confsync > CONF_UPDATE_TIME:
+            repo = getrepo()
+            if token not in repo:
+            #token错误
+                return u'Hello %s !!\n'%(ip),403
+
+    if (ip not in repo[token]['ip']) and (ip not in GIT_SERVER):
+        #ip 来源错误
+        return u'Hello %s !!!\n'%(ip),403
+
+    interval = repo[token]['interval']
+    path = repo[token]['path']
+    cmd = repo[token]['cmd']
+    cmd = ' '.join(cmd.split())#移除多余空格
+    user = repo[token]['user']
+    name = repo[token]['name']
+
+    if path.endswith('/'):
+        syndir = path.split('/')[-2]  # 显示同步目录防止出错
+    else:
+        syndir = path.split('/')[-1]  # 显示同步目录防止出错
+
+    if token in status:
+        end_time = gettime()
+        if end_time - status[token] < interval:
+            return u'同步太频繁,请稍后\n\n\n\n'
+
+    status[token]=gettime()
+
+    # 检测是否允许执行此命令执行
+    if not allow_exec(cmd):
+        return 'the cmd not in exec white list',403
+
+    # 检测是否允许在此目录执行
+    if not allow_path(path):
+        return 'The path is not allow access',403
+    # 执行命令
+    tmp = runcmd(cmd, path) + '\n\n'
+    # 更改权限
+    tmp += runcmd('chown -R {user}:{user} *'.format(user=user), path)
+    #显示结果
+    showchar ='name: {name}\nsyndir is: {syndir}\n\n{tmp}\n'.format(
+        name=name,syndir=syndir,tmp=tmp)
+
+    return showchar
+
+@app.errorhandler(404)
+def page_error(error):
+    return 'Hello World!\n',403
+
+
+
+if __name__ == '__main__':
+    app.run(host=HOST,port=PORT,debug=DEBUG)