|
- param(
- [string]$DeployEnv = $(if ($env:DEPLOY_ENV) { $env:DEPLOY_ENV } else { "emp-test" }),
- [string]$ImageNamespace = $(if ($env:IMAGE_NAMESPACE) { $env:IMAGE_NAMESPACE } else { "emp-test" }),
- [string]$ImageTag = $(if ($env:IMAGE_TAG) { $env:IMAGE_TAG } else { Get-Date -Format "yyyyMMddHHmmss" }),
- [string]$NpmRegistry = $(if ($env:NPM_REGISTRY) { $env:NPM_REGISTRY } else { "https://registry.npmjs.org" }),
- [string]$EmpRoot = $(if ($env:EMP_ROOT) { $env:EMP_ROOT } else { "" }),
- [string]$ProfileDir = $(if ($env:PROFILE_DIR) { $env:PROFILE_DIR } else { "" }),
- [switch]$SkipBuild,
- [switch]$NoMiddlewareImages,
- [switch]$CosUpload
- )
-
- $ErrorActionPreference = "Stop"
-
- # Local package script. Build images locally, export them, then run on server only with docker load + compose up.
- $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
- $RootDir = $null
- $DistDir = Join-Path $ScriptDir "dist"
- $BuildContextDir = Join-Path $ScriptDir ".build-context"
- if (-not $env:IMAGE_NAMESPACE -and $ImageNamespace -eq "emp-test" -and $DeployEnv -ne "emp-test") {
- $ImageNamespace = $DeployEnv
- }
- $PackageName = $(if ($env:PACKAGE_NAME) { $env:PACKAGE_NAME } else { "$DeployEnv-runtime-$ImageTag" })
- $PackageDir = Join-Path $DistDir $PackageName
- $PackageArchive = Join-Path $DistDir "$PackageName.tar.gz"
-
- $JavaModules = @("emp_gateway", "emp_auth", "emp_monitor", "emp_data")
- $AppImages = @(
- "$ImageNamespace/emp-gateway:$ImageTag",
- "$ImageNamespace/emp-gateway:latest",
- "$ImageNamespace/emp-auth:$ImageTag",
- "$ImageNamespace/emp-auth:latest",
- "$ImageNamespace/emp-monitor:$ImageTag",
- "$ImageNamespace/emp-monitor:latest",
- "$ImageNamespace/emp-data:$ImageTag",
- "$ImageNamespace/emp-data:latest",
- "$ImageNamespace/emp-pdf:$ImageTag",
- "$ImageNamespace/emp-pdf:latest",
- "$ImageNamespace/emp-ws:$ImageTag",
- "$ImageNamespace/emp-ws:latest",
- "$ImageNamespace/emp-admin:$ImageTag",
- "$ImageNamespace/emp-admin:latest"
- )
-
- $MiddlewareImages = @(
- $(if ($env:MYSQL_IMAGE) { $env:MYSQL_IMAGE } else { "mysql:8.0" }),
- $(if ($env:REDIS_IMAGE) { $env:REDIS_IMAGE } else { "redis:7-alpine" }),
- $(if ($env:KAFKA_IMAGE) { $env:KAFKA_IMAGE } else { "bitnami/kafka:3.7.0" }),
- $(if ($env:TDENGINE_IMAGE) { $env:TDENGINE_IMAGE } else { "tdengine/tdengine:3.3.6.0" }),
- $(if ($env:NACOS_IMAGE) { $env:NACOS_IMAGE } else { "nacos/nacos-server:v2.3.2-slim" })
- )
-
- function Write-Log {
- param([string]$Message)
- Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message"
- }
-
- function Add-KnownToolPaths {
- $KnownDirs = @(
- "C:\Program Files\Docker\Docker\resources\bin",
- "C:\Program Files\Docker\Docker",
- "$env:ProgramFiles\Docker\Docker\resources\bin"
- )
-
- foreach ($Dir in $KnownDirs) {
- if ($Dir -and (Test-Path $Dir) -and (($env:Path -split ';') -notcontains $Dir)) {
- $env:Path = "$Dir;$env:Path"
- }
- }
- }
-
- function Assert-Command {
- param([string]$Name)
- if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
- throw "Missing command: $Name"
- }
- }
-
- function Assert-DockerDaemon {
- & docker info *> $null
- if ($LASTEXITCODE -ne 0) {
- throw "Docker daemon is not running. Start Docker Desktop and wait until it is ready, then rerun this script."
- }
- }
-
- function Test-EmpRoot {
- param([string]$Path)
- return (Test-Path (Join-Path $Path "emp_server")) `
- -and (Test-Path (Join-Path $Path "emp_admin")) `
- -and (Test-Path (Join-Path $Path "emp_ws"))
- }
-
- function Resolve-EmpRoot {
- if ($EmpRoot) {
- $Resolved = (Resolve-Path $EmpRoot).Path
- if (-not (Test-EmpRoot $Resolved)) {
- throw "EMP_ROOT is invalid: $EmpRoot"
- }
- return $Resolved
- }
-
- $Candidates = @(
- (Join-Path $ScriptDir "..\.."),
- (Join-Path $ScriptDir "..\..\emp"),
- (Join-Path $ScriptDir ".."),
- (Get-Location).Path
- )
-
- foreach ($Candidate in $Candidates) {
- if (Test-Path $Candidate) {
- $Resolved = (Resolve-Path $Candidate).Path
- if (Test-EmpRoot $Resolved) {
- return $Resolved
- }
- }
- }
-
- throw "Cannot find EMP root. Set EMP_ROOT=/path/to/emp where emp_server, emp_admin and emp_ws exist."
- }
-
- function Invoke-Step {
- param(
- [string]$WorkingDirectory,
- [string]$FilePath,
- [string[]]$Arguments
- )
- Push-Location $WorkingDirectory
- try {
- & $FilePath @Arguments
- if ($LASTEXITCODE -ne 0) {
- throw "$FilePath failed, exit code: $LASTEXITCODE"
- }
- } finally {
- Pop-Location
- }
- }
-
- function Build-JavaImages {
- $ServerDir = Join-Path $RootDir "emp_server"
- Write-Log "Build backend jars"
- Invoke-Step $ServerDir "mvn" @("package", "-DskipTests", "-B", "-pl", ($JavaModules -join ","), "-am")
-
- foreach ($Module in $JavaModules) {
- $TargetDir = Join-Path $ServerDir "$Module\target"
- $Jar = Get-ChildItem $TargetDir -Filter "*.jar" |
- Where-Object { $_.Name -ne "app.jar" -and $_.Name -notlike "*original*" } |
- Select-Object -First 1
- if (-not $Jar) {
- throw "Jar not found for $Module"
- }
- Copy-Item $Jar.FullName (Join-Path $TargetDir "app.jar") -Force
- }
-
- Write-Log "Build backend images"
- Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_gateway", "-t", "$ImageNamespace/emp-gateway:$ImageTag", "-t", "$ImageNamespace/emp-gateway:latest", ".")
- Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_auth", "-t", "$ImageNamespace/emp-auth:$ImageTag", "-t", "$ImageNamespace/emp-auth:latest", ".")
- Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_monitor", "-t", "$ImageNamespace/emp-monitor:$ImageTag", "-t", "$ImageNamespace/emp-monitor:latest", ".")
- Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_data", "-t", "$ImageNamespace/emp-data:$ImageTag", "-t", "$ImageNamespace/emp-data:latest", ".")
-
- Write-Log "Build PDF image"
- Invoke-Step $ServerDir "docker" @("build", "-f", "emp_pdf/Dockerfile", "-t", "$ImageNamespace/emp-pdf:$ImageTag", "-t", "$ImageNamespace/emp-pdf:latest", "emp_pdf")
- }
-
- function Build-WsImage {
- Write-Log "Build WS/simulator image"
- $Dockerfile = Join-Path $ScriptDir "dockerfiles\emp-ws.Dockerfile"
- Invoke-Step $RootDir "docker" @(
- "build",
- "-f", $Dockerfile,
- "--build-arg", "NPM_REGISTRY=$NpmRegistry",
- "-t", "$ImageNamespace/emp-ws:$ImageTag",
- "-t", "$ImageNamespace/emp-ws:latest",
- "."
- )
- }
-
- function Build-AdminImage {
- $AdminDir = Join-Path $RootDir "emp_admin"
- Write-Log "Build frontend dist"
- Invoke-Step $AdminDir "pnpm" @("install", "--frozen-lockfile")
- Invoke-Step $AdminDir "pnpm" @("run", "build:shunfeng")
-
- Write-Log "Prepare frontend image context"
- $ContextDir = Join-Path $BuildContextDir "emp-admin"
- if (Test-Path $ContextDir) {
- Remove-Item $ContextDir -Recurse -Force
- }
- New-Item -ItemType Directory -Force $ContextDir | Out-Null
- Copy-Item (Join-Path $AdminDir "dist") (Join-Path $ContextDir "dist") -Recurse -Force
- Copy-Item (Join-Path $ScriptDir "nginx\admin.conf") (Join-Path $ContextDir "admin.conf") -Force
-
- Write-Log "Build frontend image"
- $Dockerfile = Join-Path $ScriptDir "dockerfiles\emp-admin.Dockerfile"
- Invoke-Step $ContextDir "docker" @(
- "build",
- "-f", $Dockerfile,
- "-t", "$ImageNamespace/emp-admin:$ImageTag",
- "-t", "$ImageNamespace/emp-admin:latest",
- "."
- )
- }
-
- function Prepare-Package {
- if (Test-Path $PackageDir) {
- Remove-Item $PackageDir -Recurse -Force
- }
- New-Item -ItemType Directory -Force $PackageDir | Out-Null
-
- $ComposeSource = Join-Path $ScriptDir "docker-compose.runtime.yml"
- $EnvSource = Join-Path $ScriptDir ".env.example"
- $ResolvedProfileDir = $(if ($ProfileDir) { $ProfileDir } else { Join-Path $ScriptDir "profiles\$DeployEnv" })
- $CommonProfileComposeSource = Join-Path $ScriptDir "profiles\docker-compose.yml"
- $ProfileComposeSource = Join-Path $ResolvedProfileDir "docker-compose.yml"
- $ProfileEnvSource = Join-Path $ResolvedProfileDir ".env.example"
- $ProfileEnvTxtSource = Join-Path $ResolvedProfileDir "env.txt"
- $TestComposeSource = Join-Path $ScriptDir "test\docker-compose.yml"
- $TestEnvSource = Join-Path $ScriptDir "test\env.txt"
-
- if (Test-Path $ProfileComposeSource) {
- $ComposeSource = $ProfileComposeSource
- } elseif (Test-Path $CommonProfileComposeSource) {
- $ComposeSource = $CommonProfileComposeSource
- } elseif ($DeployEnv -eq "emp-test" -and (Test-Path $TestComposeSource)) {
- $ComposeSource = $TestComposeSource
- }
-
- if (Test-Path $ProfileEnvSource) {
- $EnvSource = $ProfileEnvSource
- } elseif (Test-Path $ProfileEnvTxtSource) {
- $EnvSource = $ProfileEnvTxtSource
- } elseif ($DeployEnv -eq "emp-test" -and (Test-Path $TestEnvSource)) {
- $EnvSource = $TestEnvSource
- }
-
- Copy-Item $ComposeSource (Join-Path $PackageDir "docker-compose.yml") -Force
- Copy-Item $EnvSource (Join-Path $PackageDir ".env.example") -Force
- Copy-Item (Join-Path $ScriptDir "install.sh") (Join-Path $PackageDir "install.sh") -Force
- Copy-Item (Join-Path $ScriptDir "deploy-from-url.sh") (Join-Path $PackageDir "deploy-from-url.sh") -Force
- Copy-Item (Join-Path $ScriptDir "README.md") (Join-Path $PackageDir "README.md") -Force
- }
-
- function Save-Images {
- $Images = New-Object System.Collections.Generic.List[string]
- foreach ($Image in $AppImages) {
- $Images.Add($Image)
- }
-
- if (-not $NoMiddlewareImages) {
- Write-Log "Pull middleware images"
- foreach ($Image in $MiddlewareImages) {
- Invoke-Step $RootDir "docker" @("pull", $Image)
- $Images.Add($Image)
- }
- }
-
- Write-Log "Save images"
- Invoke-Step $RootDir "docker" @(@("save", "-o", (Join-Path $PackageDir "images.tar")) + $Images.ToArray())
- }
-
- function Archive-Package {
- New-Item -ItemType Directory -Force $DistDir | Out-Null
- if (Test-Path $PackageArchive) {
- Remove-Item $PackageArchive -Force
- }
- Invoke-Step $DistDir "tar" @("-czf", $PackageArchive, $PackageName)
- Write-Log "Package created: $PackageArchive"
- }
-
- function Publish-Package {
- if (-not $CosUpload -and $env:COS_UPLOAD -ne "1") {
- return
- }
-
- $CoscliBin = $(if ($env:COSCLI_BIN) { $env:COSCLI_BIN } else { "coscli" })
- Assert-Command $CoscliBin
-
- if (-not $env:COS_BUCKET) {
- throw "Missing COS_BUCKET"
- }
-
- $CoscliOptions = @()
- if ($env:COS_CONFIG_PATH) {
- $CoscliOptions += @("-c", $env:COS_CONFIG_PATH)
- } else {
- if (-not $env:COS_SECRET_ID) { throw "Missing COS_SECRET_ID or COS_CONFIG_PATH" }
- if (-not $env:COS_SECRET_KEY) { throw "Missing COS_SECRET_KEY or COS_CONFIG_PATH" }
- if (-not $env:COS_REGION) { throw "Missing COS_REGION" }
- $CoscliOptions += @(
- "--init-skip=true",
- "-i", $env:COS_SECRET_ID,
- "-k", $env:COS_SECRET_KEY,
- "-e", "cos.$($env:COS_REGION).myqcloud.com"
- )
- if ($env:COS_TOKEN) {
- $CoscliOptions += @("--token", $env:COS_TOKEN)
- }
- }
-
- $RunId = Get-Date -Format "yyyyMMddHHmmss"
- $CosSignExpire = $(if ($env:COS_SIGN_EXPIRE) { $env:COS_SIGN_EXPIRE } else { "604800" })
- $DefaultPrefix = "deploy/$DeployEnv/runtime/$RunId"
- $CosPrefix = $(if ($env:COS_PREFIX) { $env:COS_PREFIX } else { $DefaultPrefix }).Trim("/")
- $PackageBase = Split-Path -Leaf $PackageArchive
- $CosKey = $(if ($env:COS_KEY) { $env:COS_KEY } else { "$CosPrefix/$PackageBase" }).TrimStart("/")
- $CosUri = "cos://$($env:COS_BUCKET)/$CosKey"
- $Sha256 = (Get-FileHash $PackageArchive -Algorithm SHA256).Hash.ToLowerInvariant()
-
- Write-Log "Upload package to COS: $CosUri"
- & $CoscliBin @(@("cp", $PackageArchive, $CosUri) + $CoscliOptions)
- if ($LASTEXITCODE -ne 0) {
- throw "coscli cp failed, exit code: $LASTEXITCODE"
- }
-
- Write-Log "Generate signed URL, expire seconds: $CosSignExpire"
- $SignedOutput = & $CoscliBin @(@("signurl", $CosUri, "--time", $CosSignExpire, "--simple-output") + $CoscliOptions)
- if ($LASTEXITCODE -ne 0) {
- throw "coscli signurl failed, exit code: $LASTEXITCODE"
- }
- $SignedUrl = ($SignedOutput | Select-Object -Last 1).ToString()
-
- Write-Host ""
- Write-Host "Package: $PackageArchive"
- Write-Host "COS Key: $CosKey"
- Write-Host "SHA256: $Sha256"
- Write-Host "URL: $SignedUrl"
- Write-Host ""
- Write-Host "Target deploy command:"
- Write-Host " DEPLOY_ENV=$DeployEnv PACKAGE_SHA256=$Sha256 bash deploy-from-url.sh `"$SignedUrl`""
- Write-Host ""
- }
-
- Add-KnownToolPaths
- Assert-Command "docker"
- Assert-DockerDaemon
- Assert-Command "tar"
-
- $RootDir = Resolve-EmpRoot
- Write-Log "EMP root: $RootDir"
- Write-Log "Deploy root: $ScriptDir"
- Write-Log "Deploy env: $DeployEnv"
-
- if (-not $SkipBuild) {
- Assert-Command "mvn"
- Assert-Command "pnpm"
- Build-JavaImages
- Build-WsImage
- Build-AdminImage
- } else {
- Write-Log "Skip build, package existing local images only"
- }
-
- Prepare-Package
- Save-Images
- Archive-Package
- Publish-Package
|