背景:现场实施同事部署项目步骤繁琐、配置项容易搞错并且容易更错软件包;所以在想能不能写一个跨平台Shell/PowerShell脚本,自动完成Jdk安装、配置文件替换、服务启动等10+步骤等;经过一周的编写加调试,终于搞定,经实施同事确认,单次部署时间从2小时(人工)缩短至5分钟(脚本),实施效率提升24倍;部署流程标准化率100%,新员工培训成本至少减少70%;
deploy-wms.ps1
param(
[string]$ConfigFile = "wms-config.ps1"
)
# Error handling
$ErrorActionPreference = "Stop"
# Load configuration file
if (-Not (Test-Path $ConfigFile)) {
throw "Error: Configuration file $ConfigFile not found!"
}
. (Resolve-Path $ConfigFile)
# Validate required parameters
$requiredVars = @(
"DeployPath",
"MySqlUrl",
"BackendPackage",
"FrontendPackage",
"H5Package",
"NginxPath",
"BackendURL",
"NginxListenPort"
)
foreach ($var in $requiredVars) {
if (-Not (Get-Variable -Name $var -ErrorAction SilentlyContinue)) {
throw "Error: Missing required configuration parameter $var"
}
}
# [1/9]创建部署目录
Write-Host "[1/9] Creating deployment directory..."
$directories = @(
"$DeployPath/backend",
"$DeployPath/frontend",
"$DeployPath/h5",
"$DeployPath/nginx"
)
$directories | ForEach-Object {
New-Item -Path $_ -ItemType Directory -Force | Out-Null
}
# [2/9]安装JDK
Write-Host "[2/9] Install JDK..."
$jdkInstalled = $false
$javaVersion = (Get-Command java -ErrorAction SilentlyContinue).Version
if ($javaVersion) {
Write-Host "JDK is already installed, version: $javaVersion"
$jdkInstalled = $true
} else {
Write-Host "JDK not detected, starting installation..."
}
# If JDK is not installed, proceed with installation
if (-not $jdkInstalled) {
# Specify the path to the already downloaded JDK installer
$installerPath = ".\jdk\jdk-8u291-windows-x64.exe"
if (-Not (Test-Path $installerPath)) {
Write-Host "JDK installer not found, please check the path: $installerPath"
exit 1
}
# Install JDK
Start-Process -FilePath $installerPath -ArgumentList "/s" -Wait
# Configure environment variables
$javaHome = "C:\Program Files\Java\jdk-8"
[System.Environment]::SetEnvironmentVariable("JAVA_HOME", $javaHome, [System.EnvironmentVariableTarget]::Machine)
$env:JAVA_HOME = $javaHome
$path = [System.Environment]::GetEnvironmentVariable("Path", [System.EnvironmentVariableTarget]::Machine)
$newPath = "$javaHome\bin;" + $path
[System.Environment]::SetEnvironmentVariable("Path", $newPath, [System.EnvironmentVariableTarget]::Machine)
$env:Path = "$javaHome\bin;" + $env:Path
Write-Host "JDK installation complete and environment variables configured"
}
# [3/9]安装MySql
Write-Host "[3/9] Install MySQL..."
$mysqlInstalled = $false
if (Get-Service -Name "MySQL*" -ErrorAction SilentlyContinue) {
Write-Host "The MySQL service already exists"
$mysqlInstalled = $true
} elseif (Get-Command mysql -ErrorAction SilentlyContinue) {
Write-Host "The MySQL client has been installed。"
$mysqlInstalled = $true
}
if (-not $mysqlInstalled) {
$installerPath = ".\mysql\mysql-installer-community-8.0.34.0.msi"
if (-not (Test-Path $installerPath)) {
Write-Host "Error: The installation package cannot be found $installerPath"
exit 1
}
$installArgs = @(
"/i",
"`"$installerPath`"",
"/qn",
"/L*v `"$env:TEMP\MySQLInstall.log`"",
"ADDLOCAL=Server,Client",
"INSTALLDIR=`"C:\Program Files\MySQL\MySQL Server 8.0`"",
"PORT=3306",
"ENABLETCPIP=1",
"AGREETOLICENSE=Yes",
"/norestart"
)
# Carry out the installation
Start-Process msiexec -ArgumentList $installArgs -Wait
# Set environment variables
$mysqlBinPath = "C:\Program Files\MySQL\MySQL Server 8.0\bin"
[Environment]::SetEnvironmentVariable("Path", "$mysqlBinPath;$env:Path", "Machine")
$env:Path += ";$mysqlBinPath"
# Initialize the data directory and start the service
& "$mysqlBinPath\mysqld" --initialize-insecure --user=mysql
Start-Service -Name "MySQL80"
# Set the root password
& "$mysqlBinPath\mysql" -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;"
try {
mysql -u root -p123456 -e "SELECT @@version;" --silent
Write-Host "The MySQL installation was successful."
} catch {
Write-Host "Connection failed. Please check the log:"
Get-Content "$env:ProgramData\MySQL\MySQL Server 8.0\Data\*.err" -Tail 20 -ErrorAction SilentlyContinue
Write-Host "Complete installation log:$env:TEMP\MySQLInstall.log"
exit 1
}
}
# [4/9]部署后端服务
Write-Host "[4/9] Deploying backend services..."
if (-Not (Test-Path $BackendPackage)) {
throw "Error: Backend package $BackendPackage not found!"
}
# Stop existing backend process
$existingProcess = Get-Process -Name java -ErrorAction SilentlyContinue |
Where-Object { $_.Id -ne $pid }
if ($existingProcess) {
$existingProcess | Stop-Process -Force
}
# Clear and extract backend package
Remove-Item -Path "$DeployPath/backend/*" -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path $BackendPackage -DestinationPath "$DeployPath/backend" -Force
# Check required files
$jarFile = Get-ChildItem "$DeployPath/backend" -Filter *.jar | Select-Object -First 1
if (-Not $jarFile) {
throw "No JAR file found, please check the backend package content"
}
# update yml configuration
$configFile = Join-Path -Path $DeployPath -ChildPath "backend/config/application.yml"
if (-Not (Test-Path $configFile)) {
throw "Configuration file application.yml not found"
}
$content = Get-Content -Path $configFile -Raw -Encoding UTF8
$lines = $content -split "`r?`n"
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match "^\s*url:\s*jdbc:mariadb://.*") {
if ($lines[$i] -match "^\s*") {
$indentation = $matches[0]
} else {
$indentation = ''
}
$lines[$i] = "${indentation}url: $MySqlUrl"
}
}
$updatedContent = [string]::Join("`n", $lines)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($updatedContent)
[System.IO.File]::WriteAllBytes($configFile, $bytes)
Write-Host "Configuration file updated successfully"
# [5/9]启动后端服务
Write-Host "[5/9] Starting backend services..."
$jarFile = Get-ChildItem -Path "$DeployPath\backend" -Filter "*.jar" | Select-Object -First 1
if (-Not $jarFile) {
Write-Host "No .jar file found in directory: $($DeployPath)\backend"
exit 1
}
$javaArgs = @(
"-Dfile.encoding=UTF-8",
"-Dsun.jnu.encoding=UTF-8",
"-jar",
"`"$($jarFile.FullName)`"",
"--spring.config.location=file:`"$configFile`""
)
# Merge arguments into a string
$javaArgsString = $javaArgs -join " "
# Print arguments for debugging
Write-Host "Java Arguments: $javaArgsString"
# Define a script block to start the Java application with logging
$scriptBlock = {
param($javaArgsString, $DeployPath)
# Create logs directory
$logDir = Join-Path $DeployPath "backend/logs"
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
# Generate timestamp for log files
$stdoutLog = Join-Path $logDir "stdout_logFile.log"
$stderrLog = Join-Path $logDir "stderr_logFile.log"
# Start Java process with output redirection
Start-Process -FilePath "java" `
-ArgumentList $javaArgsString `
-NoNewWindow `
-PassThru `
-RedirectStandardOutput $stdoutLog `
-RedirectStandardError $stderrLog
}
# Start the background job with parameters
$job = Start-Job -ScriptBlock $scriptBlock -ArgumentList $javaArgsString, $DeployPath
# Wait for a moment to ensure the process starts
Start-Sleep -Seconds 5
# Get the process ID
$process = Get-Process java -ErrorAction SilentlyContinue |
Where-Object { $_.Id -ne $pid } |
Select-Object -First 1
# Check and output status
if ($null -ne $process -and -not $process.HasExited) {
$process.Id | Out-File "$DeployPath/backend/backend.pid"
Write-Host "Backend service started (PID: $($process.Id))"
Write-Host "Logs directory: $DeployPath/backend/logs/"
} else {
Write-Host "Failed to start the backend process."
exit 1
}
# Output process ID to file
$process.Id | Out-File "$DeployPath/backend/backend.pid"
# [6/9]部署前端服务
Write-Host "[6/9] Deploying frontend services..."
if (-Not (Test-Path $FrontendPackage)) {
throw "Error: Frontend package $FrontendPackage not found!"
}
Remove-Item -Path "$DeployPath/frontend/*" -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path $FrontendPackage -DestinationPath "$DeployPath/frontend" -Force
# Deploy H5
if (-Not (Test-Path $H5Package)) {
throw "Error: Frontend package $H5Package not found!"
}
Remove-Item -Path "$DeployPath/h5/*" -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path $H5Package -DestinationPath "$DeployPath/h5" -Force
#配置前端文件
$configJs = "$DeployPath/frontend/config.js"
$configH5Js = "$DeployPath/h5/static/config.js"
if (Test-Path $configJs) {
Write-Host "Configuring front-end parameters..."
try {
$originalContent = [System.Text.Encoding]::UTF8.GetString([System.IO.File]::ReadAllBytes($configJs))
$newContent = $originalContent -replace 'backendURL:.*', "backendURL: '$BackendURL',"
[System.IO.File]::WriteAllBytes($configJs, [System.Text.Encoding]::UTF8.GetBytes($newContent))
Write-Host "Front-end configuration updated successfully."
} catch {
Write-Error "Failed to update front-end configuration: $_"
}
} else {
Write-Warning "Front-end configuration file config.js not found"
}
if (Test-Path $configH5Js) {
Write-Host "Configuring h5 parameters..."
try {
$originalContent = [System.Text.Encoding]::UTF8.GetString([System.IO.File]::ReadAllBytes($configH5Js))
$newContent = $originalContent -replace 'backendURL:.*', "backendURL: '$BackendURL',"
[System.IO.File]::WriteAllBytes($configH5Js, [System.Text.Encoding]::UTF8.GetBytes($newContent))
Write-Host "h5 configuration updated successfully."
} catch {
Write-Error "Failed to update h5 configuration: $_"
}
} else {
Write-Warning "h5 configuration file config.js not found"
}
# [7/9]部署nginx服务
Write-Host "[7/9] Deploying Nginx services..."
if (-Not (Test-Path $NginxPath)) {
throw "Error: Nginx package $NginxPath not found!"
}
# Stop old process (enhanced version)
if (Get-Process -Name "nginx" -ErrorAction SilentlyContinue) {
taskkill /IM "nginx.exe" /F 2>&1 | Out-Null
}
# Clear and extract Nginx
Remove-Item -Path "$DeployPath/nginx/*" -Recurse -Force -ErrorAction SilentlyContinue
Expand-Archive -Path $NginxPath -DestinationPath "$DeployPath/nginx" -Force
#前端配置目录移到nginx目录下的html文件夹下
Copy-Item -Path "$DeployPath/frontend" -Destination "$DeployPath/nginx/nginx-1.24.0/html/PC" -Recurse -Force
Copy-Item -Path "$DeployPath/h5" -Destination "$DeployPath/nginx/nginx-1.24.0/html/PDA" -Recurse -Force
# [8/9]配置niginx
Write-Host "[8/9] Configuring Nginx services..."
# Normalize paths
$nginxLogsPath = "$DeployPath\nginx\nginx-1.24.0\logs" -replace '\\', '\'
$nginxConfPath = "$DeployPath\nginx\nginx-1.24.0\conf\wms-nginx.conf"
$nginxExePath = "$DeployPath\nginx\nginx-1.24.0\nginx.exe"
$logsPath = "$DeployPath\logs"
$tempPath = "$DeployPath\temp"
# Create necessary directories
New-Item -Path $nginxLogsPath -ItemType Directory -Force | Out-Null
$logFiles = @("access.log", "error.log", "nginx.pid")
if (-not (Test-Path $logsPath)) {
New-Item -ItemType Directory -Path $logsPath
}
foreach ($logFile in $logFiles) {
$logFilePath = "$logsPath\$logFile"
if (-not (Test-Path $logFilePath)) {
New-Item -ItemType File -Path $logFilePath
Write-Host "Created $logFile at: $logFilePath"
}
}
$tempFolders = @("client_body_temp", "fastcgi_temp", "proxy_temp", "scgi_temp", "uwsgi_temp")
if (-not (Test-Path $tempPath)) {
New-Item -ItemType Directory -Path $tempPath
}
foreach ($tempFolder in $tempFolders) {
$tempFolderPath = "$tempPath\$tempFolder"
if (-not (Test-Path $tempFolderPath)) {
New-Item -ItemType Directory -Path $tempFolderPath
Write-Host "Created $tempFolder at: $tempFolderPath"
}
}
# Generate Nginx configuration file
$nginxConf = @"
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
map `$http_user_agent` `$root_path` {
default $NginxDeployPath/nginx/nginx-1.24.0/html/PC;
~*(android|iphone|ipad|mobile) $NginxDeployPath/nginx/nginx-1.24.0/html/PDA;
}
server {
listen $NginxListenPort;
server_name localhost;
location /static/config.js {
alias $NginxDeployPath/nginx/nginx-1.24.0/html/PC/config.js;
add_header 'Access-Control-Allow-Origin' '*' always;
}
location / {
root `$root_path`;
index index.html index.htm;
try_files `$uri` `$uri`/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 1112;
server_name $BackendAddress;
location / {
root $DeployPath\backend\upload;
index index.html index.htm;
autoindex on;
}
}
}
"@
# Write configuration file
# Convert string content to byte array (without BOM)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($nginxConf)
# Write byte array to file
[System.IO.File]::WriteAllBytes($nginxConfPath, $bytes)
# Check Nginx executable
if (-not (Test-Path $nginxExePath)) {
throw "Nginx executable missing at: $nginxExePath"
}
# Test configuration file
& "$nginxExePath" -t -c "$nginxConfPath"
if ($LASTEXITCODE -ne 0) {
Write-Host "Nginx configuration test failed. Please check the configuration file."
exit $LASTEXITCODE
} else {
Write-Host "Nginx configuration test passed."
}
# [9/9]启动nginx服务
Write-Host "[9/9] Starting Nginx services..."
Start-Process -FilePath "$nginxExePath" -ArgumentList "-c $nginxConfPath" -NoNewWindow
# Wait a few seconds to ensure Nginx has enough time to start
Start-Sleep -Seconds 5
# Check Nginx process
$nginxProcess = Get-Process -Name "nginx" -ErrorAction SilentlyContinue
if ($nginxProcess) {
Write-Output "Nginx has started successfully."
} else {
Write-Output "Nginx failed to start. Please check the configuration file and logs."
}
Write-Host @"
==========================================
WMS system deployment successfully!
==========================================
"@
wms-config.ps1
# 基础部署路径
$DeployPath = "E:\wms-test-4"
$NginxDeployPath = "E:/wms-test-4"
# 后端数据库配置
$MySqlUrl = "jdbc:mariadb://127.0.0.1:3306/wms_zhixiang_03_10?characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&serverTimezone=Asia/Shanghai"
# 后端包路径
$BackendPackage = ".\wms-service-3.4.4.zhixiang.1-1.zip"
# 前端包路径
$FrontendPackage = ".\wms3.0-frontend.zip"
#前端 H5包路径
$H5Package = ".\h5.zip"
# Nginx安装路径
$NginxPath = ".\nginx-1.24.0.zip"
#后端端口
$serverPort = "8082"
$newPort = "8082"
# 后端服务地址
$BackendURL = "http://10.14.240.208:8081"
$BackendAddress = "10.14.240.208:8081"
# Nginx监听端口(前端端口)
$NginxListenPort = "8842"