前言
在实际项目开发中,并不是所有项目都会配置 CI/CD 流程,特别是在中小型团队或者公司内部测试环境中,很多时候我们仍然需要手动打包、上传和部署项目。这个过程虽然简单,但往往是重复性强且浪费时间还容易出错,尤其是当项目规模较大时,整个流程会变得非常耗时和繁琐。
为了解决这些问题,我们可以编写一个自动化脚本,将项目的打包、上传及部署等一系列步骤自动化,这样可以减少重复性工作,节省时间,提高效率。接下来,我将详细介绍如何通过 Shell 脚本来实现这一流程的自动化。
思路与流程
手动部署一个前端项目的典型步骤包括:
- 执行
npm run build
打包前端项目。 - 将打好的项目打成压缩包(如
.tar
包)。 - 使用
scp
命令将压缩包上传到服务器。 - 登录到服务器,解压并替换掉当前的前端项目包。
- 执行一些后续清理操作,必要时重启服务。
这些步骤虽然不复杂,但手动执行时耗时且容易出错,尤其是等待项目打包和文件上传时,可能会耗费大量时间。
通过编写一个自动化脚本,我们可以将上述步骤脚本化,并利用工具如 sshpass
实现免交互式的密码输入,进一步简化流程。
脚本实现
由于我使用的是 Mac 操作系统,且 Shell 脚本能很好地满足需求,因此这里选用 Shell 来实现自动化。
1. 打包前端项目
打包前端项目的命令很简单,直接通过 npm run build
即可。考虑到有些服务器上可能没有安装压缩工具,我们使用通用的 tar
命令来打包生成的 dist
文件夹。
bash
npm run build
tar -cvf dist.tar dist
2. 上传前端项目
打包完成后,我们我们使用 scp
命令上传项目到远程服务器,scp
命令用于在本地和远程服务器之间传输文件。
bash
scp dist.tar root@服务器IP:/usr/frontend
这行代码会将打包后的 dist.tar
文件传输到服务器的 /usr/frontend
目录。
3. 远程执行换包操作
在文件上传成功后,我们需要登录到服务器,解压并替换当前的前端项目包。通过 ssh
命令,我们可以远程执行这些操作。为了传递多行命令,我们使用 EOF
标记符号将脚本内容传递到服务器。
bash
ssh "root@服务器IP" "bash -s" <<EOF
$REMOTE_SCRIPT
EOF
在服务器上执行的具体脚本如下:
bash
REMOTE_SCRIPT=$(cat << EOF
#!/bin/bash
# 进入前端项目部署目录
cd /usr/frontend
# 解压 tar 包
tar -xvf dist.tar
if [ $? -ne 0 ]; then
echo "错误: dist.tar 解压失败"
exit 1
fi
# 替换掉前端项目包
rm -rf html
mv dist html
# 如果使用了 Docker,需要重启前端服务
docker restart frontend_web
EOF
)
这个脚本解压上传的 dist.tar
包,删除旧的 html
目录并将新的 dist
目录替换为 html
,然后重启 Docker 中的前端服务。
4. 传递密码
通常每次执行 ssh
和 scp
命令时都需要输入密码,为了避免每次手动输入密码,我们可以使用 sshpass
工具。sshpass
可以通过命令行传递密码,免去交互式输入的麻烦。
[!NOTE]
sshpass
是一个独立的命令行工具,需要手动安装。可以通过包管理器(如apt
,brew
)来安装:
bash
brew install sshpass # Mac 安装
apt-get install sshpass # Ubuntu/Debian 安装
安装好 sshpass
后,我们只需在 scp
和 ssh
命令前加上 sshpass
命令即可:
bash
sshpass -p "your_password" scp dist.tar root@服务器IP:/usr/frontend
sshpass -p "your_password" ssh "root@服务器IP" "bash -s" <<EOF
$REMOTE_SCRIPT
EOF
这样就可以避免每次输入密码的麻烦,脚本会自动使用提供的密码登录远程服务器。
5. 代码优化
上面只是脚本中主要的运行命令,为了运行脚本时有更好的容错和错误提示,我们对脚本做一些优化
提取常量
我们将服务器的信息,还有打包的名称全部提取成常量,方便脚本复用时更换信息,同时通过执行脚本时传入参数的方式接收服务器密码,避免明文保存密码的问题。
shell
# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# 获取脚本所在目录的上一级目录
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
# 本地 tar 文件名称
LOCAL_TAR_FILE="dist.tar"
# 远程服务器信息
REMOTE_USER="root" # 远程服务器用户
REMOTE_HOST="" # 远程服务器 IP
REMOTE_PORT="22" # 远程服务器 ssh 端口
REMOTE_PASSWORD="$1" # 远程服务器密码
REMOTE_DIR="/usr/frontend/" # 远程服务器文件传输目录
错误提示
为了避免执行命令时输出一些没什么用的信息,比如打包信息、解压信息等,我们可以通过 > /dev/null 2>&1
命令不在控制台展示日志输出,同时封装一个检查命令报错的函数。
shell
check_success() {
if [ $? -ne 0 ]; then
echo "错误: $1"
exit 1
fi
}
当执行完一个命令时,我们可以通过 check_success "xxx命令执行失败"
调用这个函数来判断上一个命令是否执行成功,没有成功则退出。
检查是否有 sshpass 命令工具
sshpass
是需要单独安装的命令行工具,所以为了照顾到没有安装或者不想安装的同事,我们可以判断当前环境是否安装了 sshpass
, 如果没有安装,或者安装了,在执行时没有传入密码,我们将通过交互式命令行进行上传和执行脚本。
shell
USE_SSHPASS=false
if command -v sshpass > /dev/null 2>&1; then
if [ -z "$REMOTE_PASSWORD" ]; then
echo "警告: 检测到 sshpass,但未提供密码。将使用常规方式登录。"
else
USE_SSHPASS=true
fi
else
echo "未检测到 sshpass,使用常规的密码提示方式。"
fi
完整脚本
下面是完整的自动化部署脚本,包含打包、上传和部署的所有步骤:
bash
#!/bin/bash
###
# @Author: 子洋
# @Date: 2024-09-30 12:11:05
# @LastEditors: 子洋
# @LastEditTime: 2024-09-30 12:39:33
# @Description: 测试环境自动发布脚本
# @FilePath: /web/publish/publish-beta.sh
###
# 获取脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# 获取脚本所在目录的上一级目录
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
# 本地 tar 文件名称
LOCAL_TAR_FILE="dist.tar"
# 远程服务器信息
REMOTE_USER="root" # 远程服务器用户
REMOTE_HOST="" # 远程服务器 IP
REMOTE_PORT="22" # 远程服务器 ssh 端口
REMOTE_PASSWORD="$1" # 远程服务器密码
REMOTE_DIR="/usr/frontend/" # 远程服务器文件传输目录
# 检查打包命令是否成功的函数
check_success() {
if [ $? -ne 0 ]; then
echo "错误: $1"
exit 1
fi
}
# 检查 sshpass 是否存在
USE_SSHPASS=false
if command -v sshpass > /dev/null 2>&1; then
if [ -z "$REMOTE_PASSWORD" ]; then
echo "警告: 检测到 sshpass,但未提供密码。将使用常规方式登录。"
else
USE_SSHPASS=true
fi
else
echo "未检测到 sshpass,使用常规的密码提示方式。"
fi
# 开始打包
echo "开始打包..."
npm run build > /dev/null 2>&1
check_success "前端打包失败"
# 删除旧的 tar 文件
rm -f $LOCAL_TAR_FILE > /dev/null 2>&1
# 生成新的 tar 文件
tar -cvf $LOCAL_TAR_FILE dist > /dev/null 2>&1
check_success "打包 dist 目录失败"
# 传输文件到服务器
echo "正在将文件传输到服务器..."
if [ "$USE_SSHPASS" = true ]; then
sshpass -p "$REMOTE_PASSWORD" scp -P $REMOTE_PORT $LOCAL_TAR_FILE $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR
else
scp -P $REMOTE_PORT $LOCAL_TAR_FILE $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR
fi
check_success "SCP 文件传输失败"
# 定义在远程服务器上执行的脚本内容
REMOTE_SCRIPT=$(cat << EOF
#!/bin/bash
cd $REMOTE_DIR
if [ ! -e "$LOCAL_TAR_FILE" ]; then
echo "$LOCAL_TAR_FILE 不存在"
exit 1
fi
tar -xvf $LOCAL_TAR_FILE > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "错误: $LOCAL_TAR_FILE 解压失败"
exit 1
fi
rm -rf html
mv dist html
docker restart frontend_web
EOF
)
# 登录服务器并执行脚本
echo "文件传输完成,正在登录服务器并执行脚本..."
if [ "$USE_SSHPASS" = true ]; then
sshpass -p "$REMOTE_PASSWORD" ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "bash -s" <<EOF
$REMOTE_SCRIPT
EOF
else
ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "bash -s" <<EOF
$REMOTE_SCRIPT
EOF
fi
check_success "远程脚本执行失败"
cd - > /dev/null 2>&1 || exit
echo "脚本执行完毕!"
命令行使用方式:
shell
sh publish-beta.sh your-password
结语
通过简单的 Shell 脚本,我们能够自动化地执行前端项目的打包、上传和部署操作,从而节省时间,减少人为错误。虽然这个脚本已经可以很好地工作,但在实际项目中,我们还可以进一步优化,比如添加错误处理、备份旧版本项目等功能。如果需要在生产环境中使用,建议加入更为完善的日志和异常处理机制。