基于Python實(shí)現(xiàn)前端自動(dòng)化打包部署

前言

人生苦短断凶,我用python~

作為一名專(zhuān)職前端開(kāi)發(fā)的我,為了幫助解決目前工作中的一些繁瑣的工作(主要是處理 excel 數(shù)據(jù))巫俺,解放程序員雙手认烁,前陣子就剛剛?cè)肓?python 的坑,畢竟也算是門(mén)工具語(yǔ)言介汹,都已經(jīng)加入少兒編程了却嗡,哈哈哈!

背景

實(shí)踐是檢驗(yàn)學(xué)習(xí)成果的唯一標(biāo)準(zhǔn)嘹承!

在我學(xué)習(xí)過(guò)程中窗价,一直琢磨著如何將學(xué)習(xí)的理論與我所掌握的知識(shí)結(jié)合起來(lái),來(lái)解決或者處理實(shí)際問(wèn)題叹卷,于是就有了 前端自動(dòng)化打包部署 的念頭撼港。

盡快近幾年,市面上關(guān)于自動(dòng)化部署的工具層出不窮豪娜,比如當(dāng)下比較流行的Jenkins餐胀,盡管如此,我還是想自己試一試~

環(huán)境配置

初學(xué)乍道瘤载,切不可眼高手低否灾,先給自己定個(gè)小目標(biāo),先實(shí)現(xiàn)一個(gè)最簡(jiǎn)單版本鸣奔。

工欲善其事墨技,必先利其器惩阶,開(kāi)發(fā)環(huán)境的配置是開(kāi)發(fā)的第一步。

關(guān)于 python 的安裝配置我就不贅述了扣汪。

為了方便測(cè)試断楷,我本地利用 VM 虛擬機(jī)安裝了 centos 系統(tǒng),安裝并配置 nginx 充當(dāng)了服務(wù)器崭别。

難點(diǎn)分析

要想實(shí)現(xiàn)打包冬筒,核心需要考慮下面2個(gè)問(wèn)題:

  • python 腳本中如何去執(zhí)行前端的打包命令npm run build(這里以vue項(xiàng)目作為測(cè)試)
  • python 腳本中如何連接服務(wù)器將打包好的問(wèn)題上傳到服務(wù)器的指定目錄中去

理論求證

通過(guò)查閱資料得知,python中的 os 模塊提供了非常豐富的方法用來(lái)處理文件和目錄茅主,其中 os模塊中的system()函數(shù)可以方便地運(yùn)行其他程序或者腳本舞痰,其語(yǔ)法如下:

os.system(command)

command 要執(zhí)行的命令,相當(dāng)于在Windowscmd窗口中輸入的命令诀姚。如果要向程序或者腳本傳遞參數(shù)响牛,可以使用空格分隔程序及多個(gè)參數(shù),該方法返回結(jié)果如果為 0赫段,則表示命令執(zhí)行成功,其它值則表示錯(cuò)誤呀打。

這樣就解決了第一個(gè)問(wèn)題。

關(guān)于服務(wù)器連接這一塊糯笙,可以使用python的一個(gè)第三方模塊 paramiko贬丛,它實(shí)現(xiàn)了SSHv2協(xié)議,允許我們直接使用SSH協(xié)議對(duì)遠(yuǎn)程服務(wù)器執(zhí)行操作给涕,關(guān)于 paramiko 的更多知識(shí)和用法瘫寝,請(qǐng)戳這里

這樣上面兩個(gè)難點(diǎn)就解決了,我們就可以開(kāi)工了稠炬。

小試牛刀

首先定義一個(gè)類(lèi) SSHConnect 后續(xù)的方法我們都會(huì)在這個(gè)類(lèi)里面完善

class SSHConnect:
    # 定義一個(gè)私有變量,用來(lái)保存ssh連接通道咪啡,初始化為None
    __transport = None

初始構(gòu)造函數(shù)

我們需要在構(gòu)造函數(shù)中定義我們需要的參數(shù)首启,初始化我們的連接

# 初始化構(gòu)造函數(shù)(主機(jī),用戶(hù)名撤摸,密碼毅桃,端口,默認(rèn)22)
def __init__(self, hostname, username, password, port=22):
    self.hostname = hostname
    self.port = port
    self.username = username
    self.password = password
    # 創(chuàng)建 ssh 連接通道
    self.connect()

建立 ssh 連接通道

我們?cè)跇?gòu)造函數(shù)中最后調(diào)用了一個(gè) connect 方法建立 ssh 連接通道准夷,現(xiàn)在我們來(lái)具體的實(shí)現(xiàn)它

# 建立ssh連接通道钥飞,并綁定在 __transport 上
def connect(self):
    try:
        # 設(shè)置SSH連接的遠(yuǎn)程主機(jī)地址和端口
        self.__transport = paramiko.Transport((self.hostname, self.port))
        # 通過(guò)用戶(hù)名和密碼連接SSH服務(wù)端
        self.__transport.connect(username=self.username, password=self.password)
    except Exception as e:
        # 連接出錯(cuò)
        print(e)

執(zhí)行打包

現(xiàn)在我們需要?jiǎng)?chuàng)建一個(gè)打包方法,執(zhí)行 npm run build 命令衫嵌,利用我們 os.system 方法读宙,入?yún)⑹?work_path 是打包項(xiàng)目所在的目錄

# 前端打包(入?yún)ork_path為項(xiàng)目目錄)
def build(self, work_path):
    # 開(kāi)始打包
    print('########### run build ############')
    # 打包命令
    cmd = 'npm run build'
    # 切換到需要項(xiàng)目目錄
    os.chdir(work_path)
    # 當(dāng)前項(xiàng)目目錄下執(zhí)行打包命令
    if os.system(cmd) == 0:
        # 打包完成
        print('########### build complete ############')

只有一點(diǎn)要注意,就是要通過(guò) os.chdir(work_path) 方法切換到項(xiàng)目的所在目錄楔绞,打包當(dāng)前的項(xiàng)目结闸。

文件上傳

打包結(jié)束后唇兑,我們需要將打包好的 dist 文件夾下的文件上傳到服務(wù)器,因此桦锄,我們需要?jiǎng)?chuàng)建一個(gè)文件上傳方法扎附,我們通過(guò) paramiko.SFTPClient 方法創(chuàng)建 sftp 來(lái)完成

該方法入?yún)⑿枰獌蓚€(gè)參數(shù),一個(gè)是本地項(xiàng)目打包后的dist路徑 local_path结耀,另一個(gè)是要上傳到服務(wù)器的目標(biāo)目錄 target_path

# 文件上傳
def upload(self, local_path, target_path):
    # 判斷路徑問(wèn)題
    if not os.path.exists(local_path):
        return print('local path is not exist')

    print('文件上傳中...')

    # 實(shí)例化一個(gè) sftp 對(duì)象,指定連接的通道
    sftp = paramiko.SFTPClient.from_transport(self.__transport)
    # 打包后的文件路徑
    local_path = os.path.join(local_path, 'dist')
    # 本地路徑轉(zhuǎn)換留夜,將windows下的 \ 轉(zhuǎn)成 /
    local_path = '/'.join(local_path.split('\\'))
    # 遞歸上傳文件
    self.upload_file(sftp, local_path, target_path)

    print('文件上傳完成...')
    # 關(guān)閉連接
    self.close()

為了方便操作,需要將 windows 中的路徑分隔符\轉(zhuǎn)成 linux 下的分隔符/

此外图甜,該方法中調(diào)用了另外兩個(gè)方法碍粥,分別是 upload_filecloseclose 方法的定義很簡(jiǎn)單具则,直接調(diào)用 __transport.close() 方法即可

# 關(guān)閉連接
def close(self):
    self.__transport.close()

考慮到我們的 static 不是文件即纲,而是一個(gè)文件夾,因此需要遞歸遍歷博肋,并將其拷貝到服務(wù)器上低斋,所以我們定義了upload_file 方法,專(zhuān)門(mén)負(fù)責(zé)這個(gè)事情匪凡。

執(zhí)行linux命令

上面我們也提到了膊畴,需要遞歸遍歷static并上傳到服務(wù)器,那么上傳到服務(wù)器的目錄結(jié)構(gòu)肯定需要跟原來(lái)的 static 保持一致病游,因此對(duì)服務(wù)器的操作肯定是不可少的唇跨,需要執(zhí)行linux命令,我們需要一個(gè) exec 方法來(lái)實(shí)現(xiàn)這個(gè)功能衬衬,入?yún)⒕褪?linux 命令

# 執(zhí)行l(wèi)inux命令
def exec(self, command):
    
    # 創(chuàng)建 ssh 客戶(hù)端
    ssh = paramiko.SSHClient()
    # 指定連接的通道
    ssh._transport = self.__transport
    
    # 調(diào)用 exec_command 方法執(zhí)行命令
    stdin, stdout, stderr = ssh.exec_command(command)
    
    # 獲取命令結(jié)果买猖,返回是二進(jìn)制,需要編碼一下
    res = stdout.read().decode('utf-8')
    # 獲取錯(cuò)誤信息
    error = stderr.read().decode('utf-8')
    
    # 如果沒(méi)出錯(cuò)
    if error.strip():
        # 返回錯(cuò)誤信息
        return error
    else:
        # 返回結(jié)果
        return res
    

現(xiàn)在你可以連接服務(wù)器測(cè)試一下滋尉,這個(gè)方法的正確性

    ssh = SSHConnect(hostname='x.x.x.x', username='root', password='xxx')
    print(ssh.exec(r'df -h'))

我們連接到服務(wù)器并嘗試調(diào)用 linux 中的 df -h 命令查看我們系統(tǒng)文件系統(tǒng)的磁盤(pán)使用情況玉控,不出意外的話,會(huì)看到控制臺(tái)返回的信息

ps:命令 df -h 前面的 r 是為了讓python解釋器不轉(zhuǎn)義

遞歸上傳文件

準(zhǔn)備工作做好以后狮惜,我們就可以來(lái)是實(shí)現(xiàn)我們的遞歸上傳的方法 upload_file 了高诺,主要是通過(guò)前面創(chuàng)建的 sftp 對(duì)象的 put 方法,將本地文件上傳到對(duì)應(yīng)的服務(wù)器中

# 遞歸上傳文件
def upload_file(self, sftp, local_path, target_path):
    # 判斷當(dāng)前路徑是否是文件夾
    if not os.path.isdir(local_path):
        # 如果是文件碾篡,獲取文件名
        file_name = os.path.basename(local_path)
        # 檢查服務(wù)器文件夾是否存在
        self.check_remote_dir(sftp, target_path)
        # 服務(wù)器創(chuàng)建文件
        target_file_path = os.path.join(target_path, file_name).replace('\\', '/')
        # 上傳到服務(wù)器
        sftp.put(local_path, target_file_path)
    else:
        # 查看當(dāng)前文件夾下的子文件
        file_list = os.listdir(local_path)
        # 遍歷子文件
        for p in file_list:
            # 拼接當(dāng)前文件路徑
            current_local_path = os.path.join(local_path, p).replace('\\', '/')
            # 拼接服務(wù)器文件路徑
            current_target_path = os.path.join(target_path, p).replace('\\', '/')
            # 如果已經(jīng)是文件虱而,服務(wù)器就不需要?jiǎng)?chuàng)建文件夾了
            if os.path.isfile(current_local_path):
                # 提取當(dāng)前文件所在的文件夾
                current_target_path = os.path.split(current_target_path)[0]
            # 遞歸判斷
            self.upload_file(sftp, current_local_path, current_target_path)

上述方法中添加了一個(gè) check_remote_dir 方法,用來(lái)檢測(cè)服務(wù)器端是否已經(jīng)存在了文件夾开泽,如果服務(wù)端沒(méi)有我們就創(chuàng)建一個(gè)牡拇,定義如下:

# 創(chuàng)建服務(wù)器文件夾
def check_remote_dir(self, sftp, target_path):
    try:
        # 判斷文件夾是否存在
        sftp.stat(target_path)
    except IOError:
        # 創(chuàng)建文件夾
        self.exec(r'mkdir -p %s ' % target_path)

非常巧妙的利用了 sftp.stat 方法查看文件信息來(lái)區(qū)分的。

合并流程,自動(dòng)發(fā)布

現(xiàn)在基本的方法我們都已經(jīng)實(shí)現(xiàn)了诅迷,接下來(lái)我們需要將它們合并到 auto_deploy 方法中佩番,真正實(shí)現(xiàn)自動(dòng)發(fā)布。

# 自動(dòng)化打包部署
def auto_deploy(self, local_path, target_path):
    # 打包構(gòu)建
    self.build(local_path)
    # 文件上傳
    self.upload(local_path, target_path)

ok~ 我們來(lái)調(diào)用 auto_deploy 方法測(cè)試一下:

if __name__ == '__main__':
    # 項(xiàng)目目錄
    project_path = r'D:\learn\vue-demo'
    # 服務(wù)器目錄
    remote_path = r'/www/project'
    
    # 實(shí)例化
    ssh = SSHConnect(hostname='x.x.x.x', username='root', password='xxx')
    # 自動(dòng)打包部署
    ssh.auto_deploy(project_path, remote_path)

如果一切順利罢杉,就可以看到控制臺(tái)輸出成功L宋贰!

再看看服務(wù)器文件是否已上傳成功滩租。

并嘗試訪問(wèn)我的主頁(yè)赋秀!

非常完美!

Congratulations! 你已經(jīng) get 了這項(xiàng)技能律想,點(diǎn)個(gè)贊吧猎莲!

服務(wù)器清空

到這里的話,我們的功能已基本完成了技即,只是還有一個(gè)小小的問(wèn)題遺留著洼,如果我們不斷的迭代優(yōu)化,那么如果我們不清除服務(wù)器的目錄的話而叼,會(huì)堆積越來(lái)越多的舊的文件身笤,占用服務(wù)器的空間,因此我們需要在打包上傳前清空一下葵陵。

不妨定義一個(gè)clear_remote_dir方法來(lái)實(shí)現(xiàn)這個(gè)功能

# 清空文件夾
def clear_remote_dir(self, target_path):
    if target_path[-1] == '/':
        cmd = f'rm -rf {target_path}*'
    else:
        cmd = f'rm -rf {target_path}/*'
    self.exec(cmd)

之后把它添加到 auto_delpoy 方法中 self.upload 之前就好了~

結(jié)語(yǔ)

勉強(qiáng)算是完成了一個(gè)小工具吧液荸,這個(gè)過(guò)程對(duì)我來(lái)說(shuō)也算是對(duì) python 的一次小小的實(shí)踐吧,也算是頗有收獲了脱篙。

對(duì)于上述的代碼娇钱,完全還可以通過(guò) sys.argv 通過(guò)命令行參數(shù)解析的方式來(lái)實(shí)現(xiàn)真正的 cmd 調(diào)用,筆者在這里就不贅述了绊困,感興趣的小伙伴可以自己去實(shí)踐一下文搂。

可以看到python 在語(yǔ)法上的簡(jiǎn)潔和優(yōu)雅,這一點(diǎn)也是讓我感覺(jué)還是挺舒服的秤朗,對(duì)我個(gè)人來(lái)說(shuō)细疚,可能后面更多是作為一門(mén)工具語(yǔ)言來(lái)使用,最大程度的去解決實(shí)際問(wèn)題川梅。

完整代碼請(qǐng)戳

參考資料

Python模塊學(xué)習(xí) - Paramiko

Python3 OS 文件/目錄方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市然遏,隨后出現(xiàn)的幾起案子贫途,更是在濱河造成了極大的恐慌,老刑警劉巖待侵,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丢早,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)怨酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)傀缩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人农猬,你說(shuō)我怎么就攤上這事赡艰。” “怎么了斤葱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵慷垮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我揍堕,道長(zhǎng)料身,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任衩茸,我火速辦了婚禮芹血,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘楞慈。我一直安慰自己幔烛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布抖部。 她就那樣靜靜地躺著说贝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慎颗。 梳的紋絲不亂的頭發(fā)上乡恕,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音俯萎,去河邊找鬼傲宜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夫啊,可吹牛的內(nèi)容都是我干的函卒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼撇眯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼报嵌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起熊榛,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锚国,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后玄坦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體血筑,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绘沉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豺总。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片车伞。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖喻喳,靈堂內(nèi)的尸體忽然破棺而出另玖,到底是詐尸還是另有隱情,我是刑警寧澤沸枯,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布日矫,位于F島的核電站,受9級(jí)特大地震影響绑榴,放射性物質(zhì)發(fā)生泄漏哪轿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一翔怎、第九天 我趴在偏房一處隱蔽的房頂上張望窃诉。 院中可真熱鬧,春花似錦赤套、人聲如沸飘痛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宣脉。三九已至,卻和暖如春剔氏,著一層夾襖步出監(jiān)牢的瞬間塑猖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工谈跛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羊苟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓感憾,卻偏偏與公主長(zhǎng)得像蜡励,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阻桅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 觀其大綱 第1章Python語(yǔ)言與Linux系統(tǒng)管理第2章Python生態(tài)工具第3章 打造命令行工具第4章文...
    周少言閱讀 1,397評(píng)論 1 3
  • 小嚶子閱讀 189評(píng)論 0 6
  • 不知作何感想凉倚,在爬上床看見(jiàn)月光的一剎那,突然就覺(jué)得自己應(yīng)該要好好賞一賞明月嫂沉,才可不辜負(fù)他就這么傾心的照應(yīng)于我稽寒,于是...
    廿十三喵閱讀 231評(píng)論 0 1
  • 今日體驗(yàn):今天核對(duì)了下各店噴漆,把結(jié)算單输瓜,電腦系統(tǒng),還有實(shí)時(shí)到賬核對(duì)了下,有問(wèn)題的及時(shí)聯(lián)系前臺(tái)尤揣,發(fā)現(xiàn)問(wèn)題解決問(wèn)題搔啊。
    A鄭淑英閱讀 215評(píng)論 0 0
  • 《如何提高效率》-790字 暖暖在某設(shè)計(jì)院上班,工作量相對(duì)飽滿(mǎn)北戏,熬夜加班屬家常便飯负芋,大家已習(xí)以為常。 這天下午嗜愈,組...
    陶心暖暖閱讀 244評(píng)論 0 1