当前位置: 首页 > 后端技术 > Python

基于Python的前端自动化打包部署

时间:2023-03-25 21:56:46 Python

前言人生苦短,我用python~作为一名全职前端开发人员,为了帮助解决目前工作中的一些繁琐工作(主要是处理excel数据),解放程序员双手,前阵子刚掉进python的坑里。毕竟也算是一种工具语言,而且已经加入了少儿编程,哈哈哈!背景实践是检验学习成果的唯一标准!在我的学习过程中,我一直在思考如何将学习的理论与所掌握的知识结合起来解决或处理实际问题,于是萌生了前端自动化打包部署的想法。就在最近几年,市面上自动化部署的工具层出不穷,比如现在比较流行的Jenkins。即便如此,我还是想自己尝试一下~环境配置的初学者,不要给自己定一个小目标。先实现最简单的版本。工欲善其事,必先利其器。开发环境的配置是开发的第一步。python的安装配置我就不赘述了。为了测试方便,我在本地使用VM虚拟机安装了centos系统,安装并配置了nginx作为服务器。难点分析为了实现打包,核心需要考虑以下两个问题:如何在python脚本中执行前端打包命令npmrunbuild(这里以vue项目作为测试)以及如何连接到python脚本中服务器上传打包问题到服务器指定目录验证理论。资料显示,python中的os模块提供了非常丰富的处理文件和目录的方法。os模块中的system()函数可以方便地运行其他程序或脚本,其语法如下:os.system(command)command所要执行的命令等同于在Windowscmd窗口中输入的命令。如果要给程序或脚本传递参数,可以用空格分隔程序和多个参数。如果该方法的返回结果为0,则表示命令执行成功,其他值表示出错。这解决了第一个问题。关于服务器连接,我们可以使用python的第三方模块paramiko,它实现了SSHv2协议,让我们可以直接使用SSH协议对远程服务器进行操作,这样就解决了上面两个难点,我们可以开始工作了。小测试先定义一个类SSHConnect,后续方法我们会在这个类中完善SSHConnect类:#定义一个私有变量存放ssh连接通道,初始化为None__transport=NoneInitialconstructor我们需要在constructor我们需要初始化连接的参数#初始化构造函数(host,username,password,port,default22)def__init__(self,hostname,username,password,port=22):self.hostname=hostnameself.port=portself.username=usernameself.password=password#Createsshconnectionchannelself.connect()建立ssh连接通道我们最后在构造函数中调用了一个connect方法来建立ssh连接通道,下面我们来具体实现一下#建立ssh连接通道并绑定__transportdefconnect(self):try:#设置SSH连接的远程主机地址和端口self.__transport=paramiko.Transport((self.hostname,self.port))#passü连接到SSH服务器的用户名和密码self.__transport.connect(username=self.username,password=self.password)exceptExceptionase:#connectionerrorprint(e)Executepackaging现在我们需要创建一个打包方法,执行npmrunbuild命令使用我们的os.system方法,入参为work_path,也就是打包后的项目所在目录。前端打包(入参work_path为项目目录)defbuild(self,work_path):#开始打包print('###########runbuild############')#打包命令cmd='npmrunbuild'#切换到需要的项目目录os.chdir(work_path)#在当前项目目录下执行打包命令ifos.system(cmd)==0:#打包完成print('###########buildcomplete############')只需要注意一点,就是通过os.chdir(work_path)方法切换到项目所在目录,并打包当前项目文件上传打包完成后,我们需要将打包好的dist文件夹下的文件上传到服务器。因此,我们需要创建一个文件上传方法。我们通过paramiko.SFTPClient方法创建sftp来完成这个方法。输入参数需要两个参数,一个是本地工程打包后的dist路径local_path,一个是要上传到服务器的目标目录target_path#Fileuploaddefupload(self,local_path,target_path):#判断路径问题ifnotos.path.exists(local_path):returnprint('localpathisnotexist')print('Fileuploading...')#实例化一个sftp对象,指定连接通道sftp=paramiko.SFTPClient.from_transport(self.__transport)#打包后的文件路径local_path=os.path.join(local_path,'dist')#本地路径转换,将windows下的\转换为/local_path='/'.join(local_path.split('\\'))#递归上传文件self.upload_file(sftp,local_path,target_path)print('文件上传完成...')#关闭连接self.close()为了操作方便,需要转换路径windows下的separator\tolinux下的separator/另外,这个方法调用了另外两个方法,分别是upload_filea和关闭。close方法的定义很简单,直接调用__transport.close()方法即可#关闭连接defclose(self):self.__transport.close()考虑到我们的static不是文件,而是文件夹,所以需要递归遍历,复制到服务器,所以我们定义了upload_file方法,专门负责这件事情。执行linux命令,我们上面说了,需要递归遍历static并上传到服务器,那么上传到服务器的目录结构必须和原来的static一致,所以服务器的操作肯定是少不了的,你需要执行linux命令,我们需要一个exec方法来实现这个功能。输入参数为linux命令执行linux命令defexec(self,command):#创建ssh客户端ssh=paramiko.SSHClient()#指定连接通道ssh._transport=self.__transport#调用exec_command方法执行thecommandstdin,stdout,stderr=ssh.exec_command(command)#获取命令结果,返回为二进制,需要对其进行编码res=stdout.read().decode('utf-8')#获取错误messageerror=stderr.read().decode('utf-8')#如果没有错误iferror.strip():#返回错误信息returnerrorelse:#返回结果returnres现在可以连接服务器了为了测试它,这个方法正确性ssh=SSHConnect(hostname='x.x.x.x',username='root',password='xxx')print(ssh.exec(r'df-h'))我们连接到服务器并尝试调用df-h命令查看我们系统文件系统的磁盘使用情况。不出意外的话,会看到控制台返回的信息ps:命令df-h前面的r是为了让python解释器不逃避递归上传文件准备工作完成后,我们就可以来了实现我们的递归上传方法upload_file,主要是通过之前创建的sftp对象的put方法将本地文件上传到对应的服务器#递归上传文件defupload_file(self,sftp,local_path,target_path):#判断当前路径是否为文件夹os.path.isdir(local_path):#如果是文件,获取文件名file_name=os.path.basename(local_path)#判断是否为服务器文件夹existsself.check_remote_dir(sftp,target_path)#服务器创建文件target_file_path=os.path.join(target_path,file_name).replace('\\','/')#上传到服务器sftp.put(local_path,target_file_path)else:#查看当前文件夹下的子文件file_list=os.listdir(local_path)#遍历file_list中p的子文件:#拼接当前文件路径current_local_path=os.path.join(local_path,p).replace('\\','/')#拼接服务器文件路径current_target_path=os.path.join(target_path,p).replace('\\','/')#如果已经是文件,如果os.path,服务器不需要创建文件夹。isfile(current_local_path):#提取当前文件所在的文件夹current_target_path=os.path.split(current_target_path)[0]#递归判断self.upload_file(sftp,current_local_path,current_target_path)在上面的方法中增加了一个check_remote_dir方法来检测服务器端是否已经存在一个文件夹。如果服务器端没有文件夹,我们将创建一个。定义如下:#创建服务器文件夹defcheck_remote_dir(self,sftp,target_path):try:#判断文件夹是否存在sftp.stat(target_path)exceptIOError:#创建文件夹self.exec(r'mkdir-p%s'%target_path)非常巧妙的使用sftp.stat方法查看文件信息来区分合并过程,自动释放现在我们已经实现了基本方法,接下来我们需要将它们合并到auto_deploy方法中,真正实现自动化发布。#自动包部署defauto_deploy(self,local_path,target_path):#打包构建self.build(local_path)#文件上传self.upload(local_path,target_path)ok~我们调用auto_deploy方法测试一下:if__name__=='__main__':#项目目录project_path=r'D:\learn\vue-demo'#服务器目录remote_path=r'/www/project'#实例化ssh=SSHConnect(hostname='x.x.x.x',username='root',password='xxx')#自动打包部署ssh.auto_deploy(project_path,remote_path)如果一切顺利,可以看到控制台输出成功!!查看服务器文件是否上传成功。并尝试访问我的主页!非常完美!恭喜!掌握了这个技能,给个赞吧!如果这里清空了服务器,我们的功能就基本完成了,但是还遗留了一个小问题。如果我们继续迭代优化,那么如果不清理服务器的目录,旧的会越来越多。该文件占用服务器空间,打包上传前需要清除。不妨定义一个clear_remote_dir方法来实现这个功能#清除文件夹defclear_remote_dir(self,target_path):iftarget_path[-1]=='/':cmd=f'rm-rf{target_path}*'else:cmd=f'rm-rf{target_path}/*'self.exec(cmd)然后在self.upload之前的auto_delpoy方法中添加~结论可以算是一个小工具,这个过程也算是一个小工具对我来说python的小练习也算是收获颇丰了。对于上面的代码,真正的cmd调用也可以通过sys.argv解析命令行参数来实现。笔者在此不再赘述,有兴趣的朋友可以自行实践。可以看出python在语法上的简洁和优雅,让我觉得很舒服。对我个人来说,可能更多的是作为一种工具语言来使用,最大程度的解决实际问题。人生苦短,我用python;