雖然已經有了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檢測 '''
