造個輪子,用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檢測 '''

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM