preface
Life is short. I use python~
As a full-time front-end developer, in order to help solve some cumbersome work in my current work (mainly dealing with excel data) and liberate programmers' hands, I just entered the pit of python a while ago. After all, it is also a tool language. I have joined children's programming. Ha ha!
background
Practice is the only standard to test learning achievements!
During my study, I have been thinking about how to combine the learning theory with my knowledge to solve or deal with practical problems, so I have the idea of front-end automatic packaging and deployment.
As soon as possible, in recent years, there have been an endless stream of tools on Automated Deployment on the market, such as Jenkins, which is more popular at present. Nevertheless, I still want to try it myself~
Environment configuration
When you are a beginner, you must not aim high and do low. Set a small goal for yourself and realize the simplest version first.
If a worker wants to do well, he must first sharpen his tools. The configuration of the development environment is the first step of development.
I won't repeat the installation configuration of python.
In order to facilitate the test, I installed centos system locally using VM virtual machine, and installed and configured nginx as the server.
Difficulty analysis
To realize packaging, the core needs to consider the following two issues:
- How to execute the front-end packaging command npm run build in the python script (vue project is taken as the test here)
- In the python script, how to connect to the server and upload the packaged problem to the specified directory of the server
Theoretical verification
According to the information, the os module in python provides rich methods to process files and directories. The system() function in the os module can easily run other programs or scripts. Its syntax is as follows:
os.system(command)
Command the command to be executed is equivalent to the command entered in the cmd window of Windows. If you want to pass parameters to a program or script, you can use spaces to separate the program and multiple parameters. If the result returned by this method is 0, it indicates that the command is executed successfully, and other values indicate an error.
This solves the first problem.
For server connection, you can use a third-party module paramiko of python, which implements the SSHv2 protocol and allows us to directly use the SSH protocol to perform operations on remote servers,
In this way, the above two difficulties will be solved and we can start work.
Small trial ox knife
First, define a class SSHConnect, and we will improve the subsequent methods in this class
class SSHConnect:
# Define a private variable to save the ssh connection channel and initialize it to None __transport = None
Initial constructor
We need to define the parameters we need in the constructor to initialize our connection
# Initialize constructor (host, user name, password, port, default 22) def __init__(self, hostname, username, password, port=22): self.hostname = hostname self.port = port self.username = username self.password = password # Create ssh connection channel self.connect()
Establish ssh connection channel
We finally call a connect method in the constructor to establish the ssh connection channel. Now let's implement it concretely.
# Establish an ssh connection channel and bind it to__ On transport def connect(self): try: # Set the remote host address and port for SSH connection self.__transport = paramiko.Transport((self.hostname, self.port)) # Connect to SSH server through user name and password self.__transport.connect(username=self.username, password=self.password) except Exception as e: # link error print(e)
Perform packaging
Now we need to create a packaging method, execute the npm run build command, and use our OS System method, the input parameter is work_path is the directory where the packaged project is located
Front end packaging (enter work_path as project directory)
def build(self, work_path):
# Start packing print('########### run build ############') # Packaging command cmd = 'npm run build' # Switch to the desired project directory os.chdir(work_path) # Execute the package command under the current project directory if os.system(cmd) == 0: # Packaging complete print('########### build complete ############')
Only one thing to note is to pass the OS The chdir (work_path) method switches to the directory where the project is located and packages the current project.
File upload
After packaging, we need to upload the files in the packaged dist folder to the server. Therefore, we need to create a file upload method through paramiko The sftpclient method creates sftp to complete
This method requires two parameters. One is the dist path local after the local project is packaged_ Path, and the other is the target directory to be uploaded to the server_ path
# File upload def upload(self, local_path, target_path): # Judgment path problem if not os.path.exists(local_path): return print('local path is not exist') print('File uploading...') # Instantiate an sftp object to specify the connected channel sftp = paramiko.SFTPClient.from_transport(self.__transport) # Packaged file path local_path = os.path.join(local_path, 'dist') # Local path conversion, convert \ under windows to/ local_path = '/'.join(local_path.split('\\')) # Recursive upload file self.upload_file(sftp, local_path, target_path) print('File upload complete...') # Close connection self.close()
To facilitate operation, you need to convert the path separator \ in windows to the separator under linux/
In addition, the other two methods are called upload_. File and close. The definition of the close method is very simple and can be called directly__ transport. The close () method is sufficient
# Close connection def close(self): self.__transport.close()
Considering that our static is not a file but a folder, we need to recursively traverse and copy it to the server, so we define upload_ The file method is specifically responsible for this.
Execute linux commands
As mentioned above, we need to recursively traverse static and upload it to the server. The directory structure uploaded to the server must be consistent with the original static. Therefore, the operation on the server must be indispensable. We need to execute linux commands. We need an exec method to realize this function. The input parameter is linux commands
Execute linux commands
def exec(self, command):
# Create ssh client ssh = paramiko.SSHClient() # Specifies the connected channel ssh._transport = self.__transport # Call Exec_ The command method executes the command stdin, stdout, stderr = ssh.exec_command(command) # Get the command result. The return is binary and needs to be encoded res = stdout.read().decode('utf-8') # Get error information error = stderr.read().decode('utf-8') # If nothing goes wrong if error.strip(): # Return error message return error else: # Return results return res
Now you can connect to the server to test the correctness of this method
ssh = SSHConnect(hostname='x.x.x.x', username='root', password='xxx') print(ssh.exec(r'df -h'))
We connect to the server and try to call the df -h command in linux to check the disk usage of our file system. If nothing happens, we will see the information returned by the console
ps: the r in front of the command df -h is to prevent the python interpreter from escaping
Recursive upload file
After the preparations are done, we can implement our recursive upload method upload_file, which mainly uploads the local file to the corresponding server through the put method of the sftp object created earlier
# Recursive upload file def upload_file(self, sftp, local_path, target_path): # Determine whether the current path is a folder if not os.path.isdir(local_path): # If it is a file, get the file name file_name = os.path.basename(local_path) # Check if the server folder exists self.check_remote_dir(sftp, target_path) # Server create file target_file_path = os.path.join(target_path, file_name).replace('\\', '/') # Upload to server sftp.put(local_path, target_file_path) else: # View sub files under the current folder file_list = os.listdir(local_path) # Traverse sub files for p in file_list: # Splice current file path current_local_path = os.path.join(local_path, p).replace('\\', '/') # Splice server file path current_target_path = os.path.join(target_path, p).replace('\\', '/') # If it is already a file, the server does not need to create a folder if os.path.isfile(current_local_path): # Extract the folder where the current file is located current_target_path = os.path.split(current_target_path)[0] # Recursive judgment self.upload_file(sftp, current_local_path, current_target_path)
A check is added to the above method_ remote_ Dir method is used to check whether a folder already exists on the server. If the server does not, we will create one. The definition is as follows:
# Create server folder def check_remote_dir(self, sftp, target_path): try: # Determine whether the folder exists sftp.stat(target_path) except IOError: # create folder self.exec(r'mkdir -p %s ' % target_path)
Very clever use of SFTP The stat method looks at the file information to distinguish.
Merge process, auto publish
Now that we have implemented the basic methods, we need to merge them into auto_ In the deploy method, automatic publishing is really realized.
# Automated packaging deployment def auto_deploy(self, local_path, target_path): # Package build self.build(local_path) # File upload self.upload(local_path, target_path)
ok ~ let's call auto_ The deploy method tests:
if __name__ == '__main__': # Project directory project_path = r'D:\learn\vue-demo' # Server directory remote_path = r'/www/project' # instantiation ssh = SSHConnect(hostname='x.x.x.x', username='root', password='xxx') # Automatic package deployment ssh.auto_deploy(project_path, remote_path)
If everything goes well, you can see that the console output is successful!!
Then check whether the server file has been uploaded successfully.
And try to visit my home page!
Perfect!
Congratulations! You've got this skill. Give it a compliment!
Server empty
At this point, our functions have been basically completed, but there is still a small problem left. If we continue iterative optimization, if we do not clear the server directory, more and more old files will accumulate and occupy the server space. Therefore, we need to clear them before packaging and uploading.
You might as well define a clear_remote_dir method to implement this function
# Empty Folder 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)
Then add it to auto_ Self. In delpoy method Just before upload~
epilogue
It's just a small tool. For me, this process is a small practice of python. It's also quite rewarding.
For the above code, you can also use sys Argv realizes the real cmd call through command line parameter parsing. I won't repeat it here. Interested partners can practice it by themselves.
I can see that python is concise and elegant in syntax, which makes me feel very comfortable. Personally, it may be used as a tool language to solve practical problems to the greatest extent.
Life is short, I use python;