使用 expect 自动化部署项目

前言

最近公司需要接手几个 .Net Core 后台项目,由于之前从来没有接触过 C# 和 .Net 开发,花了一些时间了解。后台开发的思维很多都是互通的,之前也有 PHP 和少许 Java 后台开发的经验,所以上手不需要太久,主要是业务逻辑这块需要花些功夫。但是每次部署到服务器时感觉不是很方便,每次都需要通过 dotnet publish 命令发布项目到本地,然后将发布的项目文件通过 ftp 上传到服务器,再 ssh 到服务器,将 supervisorctl 项目重启。虽然都很简单,但是每次部署都需要重复的操作,也很麻烦。程序员都是懒惰的,所用想着用将这些操作弄成自动化写到脚本,执行脚本就完成部署。最后了解到 expect 神器,通过 expect 可以将上面操作通过一个脚步完成。

基本信息

1
2
3
4
5
开发环境: macOS
代码编辑器: VSCode
服务器: CentOS
Web 服务器: nginx
进程管理工具: supervisor

expect

expect 是一个控制自动交互式脚本的工具,用非交互的方式实现了所有交互式的功能。相关文档可以参考 http://xstarcd.github.io/wiki/shell/expect.html

通过 shellexpect 写了一个自动化部署到脚本。以下为全部代码,部署分本地部署和线上部署,通过参数传递。

整个代码主要分两块:
1、通过 sftp 上传编译之后的文件到服务器
2、通过 ssh 连接到服务器,然后重启 supervisor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

#!/bin/bash
# Author by Joe

# FTP 相关配置

# 线上服务器
PRODUCT_HOST="xxx.xxx.xxx.xxx"
PRODUCT_PWD="pwd"

# 本地服务器
LOCAL_HOST="192.168.50.1"
LOCAL_PWD="123"

USER="root" #FTP用户名
HOST="" #FTP服务器地址
PASSWD="" #FTP用户密码

if [ $# != 1 ]; then
echo "USAGE: Publish to production server or local server"
echo " e.g.: $0 product or $0 local"
exit 1
fi

ENV=$1

if [ $ENV == "local" ]; then
HOST=${LOCAL_HOST}
PASSWD=${LOCAL_PWD}
ENV="Debug"
elif [ $ENV == 'product' ]; then
HOST=${PRODUCT_HOST}
PASSWD=${PRODUCT_PWD}
ENV="Product"
else
echo "参数必须为 local 或者 product"
exit 1
fi

set timeout 30

echo -e "\033[47;30mINFO: 准备发布到 $ENV 服务器...\033[0m"
DEST_DIR="/root" #远程FTP服务器目录
SHELL_FOLDER=$(pwd) # 当前目录
LOC_DIR="${SHELL_FOLDER}/Servers/bin/${ENV}/netcoreapp2.1/Game" #对应的本地服务器目录
DIR_NAME="Game" # 线上服务器项目文件夹名

# echo $HOST
# echo $PASSWD
# echo $LOC_DIR
# exit 1;

echo -e "\033[47;30mINFO: 正在登录 SFTP 服务器...\033[0m"

expect <<-END

spawn sftp $USER@$HOST
expect {
"(yes/no)?" {
send "yes\r"
}
"*assword:" {send "${PASSWD}\r"}
}
expect "sftp>"
send "cd ${DEST_DIR}\r"
expect "sftp>"
send "ls\r"

# 删除旧版本
expect "sftp>"
send "rm ${DIR_NAME}/Config/*\r"

expect "sftp>"
send "rmdir ${DIR_NAME}/Config\r"

expect "sftp>"
send "rm ${DIR_NAME}/*\r"

# 删除目录
expect "sftp>"
send "rmdir ${DIR_NAME}\r"

# 上传本地文件到服务器
expect "sftp>"
send "put -r ${LOC_DIR}\r"

# 重命名为目标文件夹名
# expect "sftp>"
# send "rename publish ${DIR_NAME}\r"

# 退出
expect "sftp>"
send "quit\r"

END

PWD=${PASSWD}
SupervisorName=YGame

echo -e "\033[47;30mINFO: 正在登录远程服务器,准备重启 supervisor..\033[0m"

expect <<-END

spawn ssh [email protected]${HOST}

expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "${PWD}\r" }
}

expect "]# "

send "pwd\r"

send "supervisorctl status\r"
send "supervisorctl restart ${SupervisorName}\r"
send "tail -n 15 ${DEST_DIR}/${DIR_NAME}/Server.log\r"
send "exit\r"

expect eof

END

echo -e "\033[47;30mINFO: 操作完成 !!! .\033[0m"

自动部署

通过 VSCode 写两个 task,任务一负责编译并将文件发布到本地,任务二则是调用该脚本,下次编辑完直接调用 VSCode task 即可,以下为 VSCode Task 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
{
"version": "2.0.0",
"tasks": [
{
"label": "publish YGame Product",
"command": "dotnet",
"type": "process",
"args": [
"msbuild",
"${workspaceFolder}/YGameServer/YGameServer.csproj",
"/p:Configuration=Product",
"/p:DeployOnBuild=true",
"/p:PublishProfile=${workspaceFolder}/YGameServer/Properties/PublishProfiles/Product.pubxml"
],
"problemMatcher": "$msCompile",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true
}
},
{
"label": "publish YGame Debug",
"command": "dotnet",
"type": "process",
"args": [
"msbuild",
"${workspaceFolder}/YGameServer/YGameServer.csproj",
"/p:Configuration=Debug",
"/p:DeployOnBuild=true",
"/p:PublishProfile=${workspaceFolder}/YGameServer/Properties/PublishProfiles/Debug.pubxml"
],
"problemMatcher": "$msCompile",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": true
}
},
{
"label": "deploy YGame to Remote Server",
"type": "shell",
"command": "${workspaceFolder}/deploy.sh",
"args": ["product"],
"presentation": {
"reveal": "always",
"panel": "new"
},
"dependsOn": ["publish YGame Product"]
},
{
"label": "deploy YGame to Local Test Server",
"type": "shell",
"command": "${workspaceFolder}/deploy.sh",
"args": ["local"],
"presentation": {
"reveal": "always",
"panel": "new"
},
"dependsOn": ["publish YGame Debug"]
}
]
}
感谢您的阅读,本文由 Joe 版权所有。如若转载,请注明出处:Joe(http://aidevjoe.github.io/2019/03/19/使用-expect-自动化部署项目/
升级 macOS 自带 bash 到最新版本
将 SVN 仓库转换成 Git 仓库