• 造个轮子,用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检测 '''
  • 相关阅读:
    XMLhttp.status返回值及xmlhttp.readState值
    移动端meta设置
    css自定义checkbox样式
    base.css(css基础样式)
    css文本块中首行文本的缩进,字间距
    jq里的 ajax( ) 方法
    小程序 背景图在开发工具上显示,但是在真机调试时无效
    小程序登陆锁-登录逻辑
    背景图尺寸(background-size)
    动态渲染style 背景图片
  • 原文地址:https://www.cnblogs.com/ch459742906/p/8135759.html
Copyright © 2020-2023  润新知