zoukankan      html  css  js  c++  java
  • 造个轮子,用python写的web项目自动部署系统

    虽然已经有了Jenkis等强大的持续集成系统,但仍阻挡不了我对造轮子的热爱。

    适用框架:Thinkphp,正增加对Laravel的支持

    功能:将项目代码进行版本控制,便于保存旧版本,快速切换不同版本。

    优点:无需安装!配置超简单!上线快!

    要求:

    1.备份你的线上代码,以防万一

    2.将新的项目目录使用zip压缩

    3.第一次使用,需要把项目根目录设置为软链接到某空目录,此空目录权限需和项目目录应该有的访问权限相同,随后程序会自动将项目根目录软链接到新的项目路径

    4.第3步设置完成后,直接运行以下代码,根据提示配置即可。

    流程概述:

    1.解压项目zip文件,以project.zip举例

    2.将解压后的项目目录(project)以版本号的形式更改名字(project.201710011300),移动到版本库(path_to_appstore/project/project.201710011300)

    3.对project.201710011300进行处理,包括更改owner,清除误上传的缓存(如有)

    4.将项目根目录(path_to_app_base)软链接到该版本号目录(ln  -sfn  path_to_appstore/project/project.201710011300   path_to_app_base)

    5.将该版本的特定目录(如用户上传文件),软链接到指定线上目录((ln  -sfn  path_to_appstore/project/project.201710011300/special_path   path_to_project_special)

    #!/usr/bin/python
    # -*- coding: UTF-8 -*- 
    import os,time,subprocess,shutil
    
    defaultAppName='project.haha.com' #项目名
    defaultStaticFolderName='Uploads' #用户上传静态文件夹默认名
    appOwner='www:www' #默认文件所有者
    defaultAppParentPath='/data/wwwroot/' #项目所在目录
    defaultAppPath=defaultAppParentPath+defaultAppName #项目路径
    defaultAppStaticPath=defaultAppParentPath+defaultAppName+'.'+defaultStaticFolderName #项目的特殊资源目录
    newFileDir='' #上传文件所在路径
    defaultNewFile='upload.zip' #上传文件名
    newFile='' #根据用户输入判断
    factoryDir=defaultAppParentPath+'/factoryDir' #存放上传的zip文件
    newFileUnzipedWorkDir=factoryDir+'/'+'unziped' #在此目录中对上传文件进行解压等操作,以防万一
    appBackDir=defaultAppParentPath+'/appback/'+defaultAppName+'/' #项目的备份文件夹
    appStoreParent=defaultAppParentPath+'appstore/' #项目版本库的上级文件夹
    appStore=appStoreParent+defaultAppName+'/' #项目版本库
    appCleanRuntimeDir=['/Runtime/Cache','/Runtime/Data','/Runtime/Temp'] #需要清除的缓存路径
    
    timeSign=time.strftime("%a-%b-%d-%H-%M-%S-%Y", time.localtime()) #随后文件中使用的时间标记(如版本号)
    
    #确定上传文件所在目录并验证 
    def judgePath():
        global newFileDir,defaultAppPath
        print "压缩包默认已上传至"+defaultAppPath
        newFileDir=raw_input("如果是以上目录,请press enter,如果不是,请输入存放目录:")
        if newFileDir=='':
            newFileDir=defaultAppPath
        if os.path.isdir(newFileDir)==False:
            print "亲,目录并不存在啊 ( >﹏<。)"
            return judgePath()
        if os.access(newFileDir, os.W_OK)==False:
            print '亲,我木有权限进入:'+newFileDir+' ( >﹏<。)'
            return judgePath()
        else:
            return os.path.abspath(newFileDir)+'/' # abspath:返回path规范化的绝对路径
    
    #确定上传的文件名并验证
    def judegeNewFile():
        global newFile
        newFile=raw_input("默认zip文件是"+defaultNewFile+",如果是,请press enter,如果不是,请输入文件名:")
        if newFile=='':
            newFile=defaultNewFile
        if os.path.exists(newFileDir+newFile)==False:
            print '亲,'+newFileDir+newFile+'并不存在啊 ( >﹏<。)'
            return judegeNewFile()
        if os.access(newFileDir+newFile, os.W_OK)==False:
            print '亲,我木有写权限:'+newFile+' ( >﹏<。)'
            return judegeNewFile()
        else:
            return newFile
    #start...
    #删除zip文件工作目录
    if os.path.isdir(newFileUnzipedWorkDir)==True:
        shutil.rmtree(newFileUnzipedWorkDir)
    os.makedirs(newFileUnzipedWorkDir)
    
    
    print '***注意***,默认操作的项目是:'+defaultAppName
    #获取上传文件所在的路径
    newFileDir= judgePath()
    #获取上传文件的名字
    newFile= judegeNewFile()
    
    filePath=newFileDir+newFile #上传文件路径
    
    #移动文件到处理目录并改名
    print ''+newFile+'移动至'+factoryDir+'进行处理...'
    newFilePath=factoryDir+'/'+newFile
    shutil.move(filePath,newFilePath) 
    print "已将zip文件移动至"+newFileUnzipedWorkDir
    
    #解压文件
    print '开始解压文件...'
    pUnzip=subprocess.Popen(["unzip",newFilePath,'-d',newFileUnzipedWorkDir], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) 
    
    #注意,如果使用stderr但不使用stdin,那unzip时是不行的,为什么?万能的网友请告诉我
    '''Popen.communicate(input=None)
    Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child.
    
    communicate() returns a tuple (stdoutdata, stderrdata).
      
    Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other than None in the result tuple, you need to give stdout=PIPE and/or stderr=PIPE too.
    '''
    
    tupleUnzip = pUnzip.communicate('A')
    if tupleUnzip[-1]=='':
        print "解压文件完毕"
    else:
        print "解压出错了.错误为:"+tupleUnzip[-1]
        exit()
    unzipedFileList = os.listdir(newFileUnzipedWorkDir)
    if len(unzipedFileList)>1:
        print "解压后发现上传文件并不是一个文件夹,目前功能很low,请压缩整个文件夹再上传"
        exit()
    afterUnzipedFindThisFolder=unzipedFileList[0];#解压完后,发现的文件夹名字
    
    
    #把新项目文件放到版本库中
    thisVerName=defaultAppName+"."+timeSign
    #在压缩文件处理车间处理过的文件放到appstore中并重命名
    '''
    tips
    mv用法 格式:mv dir1 dir2 如果目录dir2不存在,将目录dir1改名为dir2;否则,将dir1移动到dir2中。 ''' print "准备把处理好的解压文件放到"+appStore+'/'+thisVerName shutil.move(newFileUnzipedWorkDir+'/'+afterUnzipedFindThisFolder,appStore+'/'+thisVerName) print "已将处理好的解压文件放到"+appStore+'/'+thisVerName #删除缓存 for dir in appCleanRuntimeDir: absDir=appStore+'/'+thisVerName+'/'+dir if (os.path.exists(absDir)): #清理 try: shutil.rmtree(absDir) print "已删除"+absDir except OSError,e: print "删除失败!!!"+str(e) else: print '缓存目录'+appStore+'/'+dir+'不存在,无法清理' #更改文件所有者 pChown=subprocess.Popen(["chown",appOwner,appStore+'/'+thisVerName,'-R'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) tupleChown = pChown.communicate(); if tupleChown[-1]=='': print "已将文件夹的所有者改为:"+appOwner else: print "更改所有者失败.错误为:"+tupleChown[-1] exit() #创建软连接 print "开始创建软链接"
    #1.创建整个项目的软链接
    pLn=subprocess.Popen(["ln",'-sfn',appStore+'/'+thisVerName,defaultAppPath], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) tupleLn = pLn.communicate() if tupleLn[-1]=='': print "已将"+defaultAppName+'软链接到:'+appStore+'/'+thisVerName else: print "创建软链接错误.错误为:"+tupleChown[-1] exit() #2.创建特殊目录软连接 shutil.move(appStore+'/'+thisVerName+"/"+defaultStaticFolderName,appStore+'/'+thisVerName+"/"+defaultStaticFolderName+'.bak') if (os.path.exists(defaultAppStaticPath)==False): print '发现'+defaultAppStaticPath+'不存在,准备复制' os.makedirs(defaultAppStaticPath) #TODO:这里是异步,因此需要等待一秒,有待改进 time.sleep(1) print '准备复制的文件夹为'+appStore+'/'+thisVerName+"/"+defaultStaticFolderName+'.bak' print '最终目录为'+defaultAppStaticPath #由于subprocess中无法识别星号*,所以暂时放弃了 #cp -R ./Public.bak/* /webserver/wwwroot/Public #pCpStatic=subprocess.Popen(["cp",'-R',appStore+'/'+thisVerName+"/"+defaultStaticFolderName+'.bak'+'/*',defaultAppStaticPath], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) #tupleCpStatic = pCpStatic.communicate() #if tupleCpStatic[-1]=='': # print "已将"+appStore+'/'+thisVerName+"/"+defaultStaticFolderName+'复制到链接到:'+defaultAppStaticPath #else: # print "复制错误.错误为:"+tupleCpStatic[-1] # exit() os.popen('cp -R '+appStore+'/'+thisVerName+"/"+defaultStaticFolderName+'.bak'+'/* '+defaultAppStaticPath) pLnStatic=subprocess.Popen(["ln",'-sfn',defaultAppStaticPath,appStore+'/'+thisVerName+"/"+defaultStaticFolderName], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE) tupleLnStatic = pLnStatic.communicate() if tupleLnStatic[-1]=='': print "已将"+defaultAppStaticPath+'软链接到:'+appStore+'/'+thisVerName+"/"+defaultStaticFolderName else: print "特殊目录创建软链接错误.错误为:"+tupleLnStatic[-1] exit() ''' TODO: 1.严格限制各个文件夹权限,满足访问的基础上,尽可能最小化权限 2.解压后确定是根目录 1.当前项目配置中的app_debug检测 '''
  • 相关阅读:
    单例模式
    HashSet、LinkedHashSet、SortedSet、TreeSet
    ArrayList、LinkedList、CopyOnWriteArrayList
    HashMap、Hashtable、LinkedHashMap
    andrew ng machine learning week8 非监督学习
    andrew ng machine learning week7 支持向量机
    andrew ng machine learning week6 机器学习算法理论
    andrew ng machine learning week5 神经网络
    andrew ng machine learning week4 神经网络
    vue组件监听属性变化watch方法报[Vue warn]: Method "watch" has type "object" in the component definition. Did you reference the function correctly?
  • 原文地址:https://www.cnblogs.com/ch459742906/p/8135759.html
Copyright © 2011-2022 走看看