Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

356 строки
13 KiB

  1. param(
  2. [string]$DeployEnv = $(if ($env:DEPLOY_ENV) { $env:DEPLOY_ENV } else { "emp-test" }),
  3. [string]$ImageNamespace = $(if ($env:IMAGE_NAMESPACE) { $env:IMAGE_NAMESPACE } else { "emp-test" }),
  4. [string]$ImageTag = $(if ($env:IMAGE_TAG) { $env:IMAGE_TAG } else { Get-Date -Format "yyyyMMddHHmmss" }),
  5. [string]$NpmRegistry = $(if ($env:NPM_REGISTRY) { $env:NPM_REGISTRY } else { "https://registry.npmjs.org" }),
  6. [string]$EmpRoot = $(if ($env:EMP_ROOT) { $env:EMP_ROOT } else { "" }),
  7. [string]$ProfileDir = $(if ($env:PROFILE_DIR) { $env:PROFILE_DIR } else { "" }),
  8. [switch]$SkipBuild,
  9. [switch]$NoMiddlewareImages,
  10. [switch]$CosUpload
  11. )
  12. $ErrorActionPreference = "Stop"
  13. # Local package script. Build images locally, export them, then run on server only with docker load + compose up.
  14. $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
  15. $RootDir = $null
  16. $DistDir = Join-Path $ScriptDir "dist"
  17. $BuildContextDir = Join-Path $ScriptDir ".build-context"
  18. if (-not $env:IMAGE_NAMESPACE -and $ImageNamespace -eq "emp-test" -and $DeployEnv -ne "emp-test") {
  19. $ImageNamespace = $DeployEnv
  20. }
  21. $PackageName = $(if ($env:PACKAGE_NAME) { $env:PACKAGE_NAME } else { "$DeployEnv-runtime-$ImageTag" })
  22. $PackageDir = Join-Path $DistDir $PackageName
  23. $PackageArchive = Join-Path $DistDir "$PackageName.tar.gz"
  24. $JavaModules = @("emp_gateway", "emp_auth", "emp_monitor", "emp_data")
  25. $AppImages = @(
  26. "$ImageNamespace/emp-gateway:$ImageTag",
  27. "$ImageNamespace/emp-gateway:latest",
  28. "$ImageNamespace/emp-auth:$ImageTag",
  29. "$ImageNamespace/emp-auth:latest",
  30. "$ImageNamespace/emp-monitor:$ImageTag",
  31. "$ImageNamespace/emp-monitor:latest",
  32. "$ImageNamespace/emp-data:$ImageTag",
  33. "$ImageNamespace/emp-data:latest",
  34. "$ImageNamespace/emp-pdf:$ImageTag",
  35. "$ImageNamespace/emp-pdf:latest",
  36. "$ImageNamespace/emp-ws:$ImageTag",
  37. "$ImageNamespace/emp-ws:latest",
  38. "$ImageNamespace/emp-admin:$ImageTag",
  39. "$ImageNamespace/emp-admin:latest"
  40. )
  41. $MiddlewareImages = @(
  42. $(if ($env:MYSQL_IMAGE) { $env:MYSQL_IMAGE } else { "mysql:8.0" }),
  43. $(if ($env:REDIS_IMAGE) { $env:REDIS_IMAGE } else { "redis:7-alpine" }),
  44. $(if ($env:KAFKA_IMAGE) { $env:KAFKA_IMAGE } else { "bitnami/kafka:3.7.0" }),
  45. $(if ($env:TDENGINE_IMAGE) { $env:TDENGINE_IMAGE } else { "tdengine/tdengine:3.3.6.0" }),
  46. $(if ($env:NACOS_IMAGE) { $env:NACOS_IMAGE } else { "nacos/nacos-server:v2.3.2-slim" })
  47. )
  48. function Write-Log {
  49. param([string]$Message)
  50. Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Message"
  51. }
  52. function Add-KnownToolPaths {
  53. $KnownDirs = @(
  54. "C:\Program Files\Docker\Docker\resources\bin",
  55. "C:\Program Files\Docker\Docker",
  56. "$env:ProgramFiles\Docker\Docker\resources\bin"
  57. )
  58. foreach ($Dir in $KnownDirs) {
  59. if ($Dir -and (Test-Path $Dir) -and (($env:Path -split ';') -notcontains $Dir)) {
  60. $env:Path = "$Dir;$env:Path"
  61. }
  62. }
  63. }
  64. function Assert-Command {
  65. param([string]$Name)
  66. if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
  67. throw "Missing command: $Name"
  68. }
  69. }
  70. function Assert-DockerDaemon {
  71. & docker info *> $null
  72. if ($LASTEXITCODE -ne 0) {
  73. throw "Docker daemon is not running. Start Docker Desktop and wait until it is ready, then rerun this script."
  74. }
  75. }
  76. function Test-EmpRoot {
  77. param([string]$Path)
  78. return (Test-Path (Join-Path $Path "emp_server")) `
  79. -and (Test-Path (Join-Path $Path "emp_admin")) `
  80. -and (Test-Path (Join-Path $Path "emp_ws"))
  81. }
  82. function Resolve-EmpRoot {
  83. if ($EmpRoot) {
  84. $Resolved = (Resolve-Path $EmpRoot).Path
  85. if (-not (Test-EmpRoot $Resolved)) {
  86. throw "EMP_ROOT is invalid: $EmpRoot"
  87. }
  88. return $Resolved
  89. }
  90. $Candidates = @(
  91. (Join-Path $ScriptDir "..\.."),
  92. (Join-Path $ScriptDir "..\..\emp"),
  93. (Join-Path $ScriptDir ".."),
  94. (Get-Location).Path
  95. )
  96. foreach ($Candidate in $Candidates) {
  97. if (Test-Path $Candidate) {
  98. $Resolved = (Resolve-Path $Candidate).Path
  99. if (Test-EmpRoot $Resolved) {
  100. return $Resolved
  101. }
  102. }
  103. }
  104. throw "Cannot find EMP root. Set EMP_ROOT=/path/to/emp where emp_server, emp_admin and emp_ws exist."
  105. }
  106. function Invoke-Step {
  107. param(
  108. [string]$WorkingDirectory,
  109. [string]$FilePath,
  110. [string[]]$Arguments
  111. )
  112. Push-Location $WorkingDirectory
  113. try {
  114. & $FilePath @Arguments
  115. if ($LASTEXITCODE -ne 0) {
  116. throw "$FilePath failed, exit code: $LASTEXITCODE"
  117. }
  118. } finally {
  119. Pop-Location
  120. }
  121. }
  122. function Build-JavaImages {
  123. $ServerDir = Join-Path $RootDir "emp_server"
  124. Write-Log "Build backend jars"
  125. Invoke-Step $ServerDir "mvn" @("package", "-DskipTests", "-B", "-pl", ($JavaModules -join ","), "-am")
  126. foreach ($Module in $JavaModules) {
  127. $TargetDir = Join-Path $ServerDir "$Module\target"
  128. $Jar = Get-ChildItem $TargetDir -Filter "*.jar" |
  129. Where-Object { $_.Name -ne "app.jar" -and $_.Name -notlike "*original*" } |
  130. Select-Object -First 1
  131. if (-not $Jar) {
  132. throw "Jar not found for $Module"
  133. }
  134. Copy-Item $Jar.FullName (Join-Path $TargetDir "app.jar") -Force
  135. }
  136. Write-Log "Build backend images"
  137. Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_gateway", "-t", "$ImageNamespace/emp-gateway:$ImageTag", "-t", "$ImageNamespace/emp-gateway:latest", ".")
  138. Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_auth", "-t", "$ImageNamespace/emp-auth:$ImageTag", "-t", "$ImageNamespace/emp-auth:latest", ".")
  139. Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_monitor", "-t", "$ImageNamespace/emp-monitor:$ImageTag", "-t", "$ImageNamespace/emp-monitor:latest", ".")
  140. Invoke-Step $ServerDir "docker" @("build", "-f", "Dockerfile.service", "--build-arg", "MODULE=emp_data", "-t", "$ImageNamespace/emp-data:$ImageTag", "-t", "$ImageNamespace/emp-data:latest", ".")
  141. Write-Log "Build PDF image"
  142. Invoke-Step $ServerDir "docker" @("build", "-f", "emp_pdf/Dockerfile", "-t", "$ImageNamespace/emp-pdf:$ImageTag", "-t", "$ImageNamespace/emp-pdf:latest", "emp_pdf")
  143. }
  144. function Build-WsImage {
  145. Write-Log "Build WS/simulator image"
  146. $Dockerfile = Join-Path $ScriptDir "dockerfiles\emp-ws.Dockerfile"
  147. Invoke-Step $RootDir "docker" @(
  148. "build",
  149. "-f", $Dockerfile,
  150. "--build-arg", "NPM_REGISTRY=$NpmRegistry",
  151. "-t", "$ImageNamespace/emp-ws:$ImageTag",
  152. "-t", "$ImageNamespace/emp-ws:latest",
  153. "."
  154. )
  155. }
  156. function Build-AdminImage {
  157. $AdminDir = Join-Path $RootDir "emp_admin"
  158. Write-Log "Build frontend dist"
  159. Invoke-Step $AdminDir "pnpm" @("install", "--frozen-lockfile")
  160. Invoke-Step $AdminDir "pnpm" @("run", "build:shunfeng")
  161. Write-Log "Prepare frontend image context"
  162. $ContextDir = Join-Path $BuildContextDir "emp-admin"
  163. if (Test-Path $ContextDir) {
  164. Remove-Item $ContextDir -Recurse -Force
  165. }
  166. New-Item -ItemType Directory -Force $ContextDir | Out-Null
  167. Copy-Item (Join-Path $AdminDir "dist") (Join-Path $ContextDir "dist") -Recurse -Force
  168. Copy-Item (Join-Path $ScriptDir "nginx\admin.conf") (Join-Path $ContextDir "admin.conf") -Force
  169. Write-Log "Build frontend image"
  170. $Dockerfile = Join-Path $ScriptDir "dockerfiles\emp-admin.Dockerfile"
  171. Invoke-Step $ContextDir "docker" @(
  172. "build",
  173. "-f", $Dockerfile,
  174. "-t", "$ImageNamespace/emp-admin:$ImageTag",
  175. "-t", "$ImageNamespace/emp-admin:latest",
  176. "."
  177. )
  178. }
  179. function Prepare-Package {
  180. if (Test-Path $PackageDir) {
  181. Remove-Item $PackageDir -Recurse -Force
  182. }
  183. New-Item -ItemType Directory -Force $PackageDir | Out-Null
  184. $ComposeSource = Join-Path $ScriptDir "docker-compose.runtime.yml"
  185. $EnvSource = Join-Path $ScriptDir ".env.example"
  186. $ResolvedProfileDir = $(if ($ProfileDir) { $ProfileDir } else { Join-Path $ScriptDir "profiles\$DeployEnv" })
  187. $CommonProfileComposeSource = Join-Path $ScriptDir "profiles\docker-compose.yml"
  188. $ProfileComposeSource = Join-Path $ResolvedProfileDir "docker-compose.yml"
  189. $ProfileEnvSource = Join-Path $ResolvedProfileDir ".env.example"
  190. $ProfileEnvTxtSource = Join-Path $ResolvedProfileDir "env.txt"
  191. $TestComposeSource = Join-Path $ScriptDir "test\docker-compose.yml"
  192. $TestEnvSource = Join-Path $ScriptDir "test\env.txt"
  193. if (Test-Path $ProfileComposeSource) {
  194. $ComposeSource = $ProfileComposeSource
  195. } elseif (Test-Path $CommonProfileComposeSource) {
  196. $ComposeSource = $CommonProfileComposeSource
  197. } elseif ($DeployEnv -eq "emp-test" -and (Test-Path $TestComposeSource)) {
  198. $ComposeSource = $TestComposeSource
  199. }
  200. if (Test-Path $ProfileEnvSource) {
  201. $EnvSource = $ProfileEnvSource
  202. } elseif (Test-Path $ProfileEnvTxtSource) {
  203. $EnvSource = $ProfileEnvTxtSource
  204. } elseif ($DeployEnv -eq "emp-test" -and (Test-Path $TestEnvSource)) {
  205. $EnvSource = $TestEnvSource
  206. }
  207. Copy-Item $ComposeSource (Join-Path $PackageDir "docker-compose.yml") -Force
  208. Copy-Item $EnvSource (Join-Path $PackageDir ".env.example") -Force
  209. Copy-Item (Join-Path $ScriptDir "install.sh") (Join-Path $PackageDir "install.sh") -Force
  210. Copy-Item (Join-Path $ScriptDir "deploy-from-url.sh") (Join-Path $PackageDir "deploy-from-url.sh") -Force
  211. Copy-Item (Join-Path $ScriptDir "README.md") (Join-Path $PackageDir "README.md") -Force
  212. }
  213. function Save-Images {
  214. $Images = New-Object System.Collections.Generic.List[string]
  215. foreach ($Image in $AppImages) {
  216. $Images.Add($Image)
  217. }
  218. if (-not $NoMiddlewareImages) {
  219. Write-Log "Pull middleware images"
  220. foreach ($Image in $MiddlewareImages) {
  221. Invoke-Step $RootDir "docker" @("pull", $Image)
  222. $Images.Add($Image)
  223. }
  224. }
  225. Write-Log "Save images"
  226. Invoke-Step $RootDir "docker" @(@("save", "-o", (Join-Path $PackageDir "images.tar")) + $Images.ToArray())
  227. }
  228. function Archive-Package {
  229. New-Item -ItemType Directory -Force $DistDir | Out-Null
  230. if (Test-Path $PackageArchive) {
  231. Remove-Item $PackageArchive -Force
  232. }
  233. Invoke-Step $DistDir "tar" @("-czf", $PackageArchive, $PackageName)
  234. Write-Log "Package created: $PackageArchive"
  235. }
  236. function Publish-Package {
  237. if (-not $CosUpload -and $env:COS_UPLOAD -ne "1") {
  238. return
  239. }
  240. $CoscliBin = $(if ($env:COSCLI_BIN) { $env:COSCLI_BIN } else { "coscli" })
  241. Assert-Command $CoscliBin
  242. if (-not $env:COS_BUCKET) {
  243. throw "Missing COS_BUCKET"
  244. }
  245. $CoscliOptions = @()
  246. if ($env:COS_CONFIG_PATH) {
  247. $CoscliOptions += @("-c", $env:COS_CONFIG_PATH)
  248. } else {
  249. if (-not $env:COS_SECRET_ID) { throw "Missing COS_SECRET_ID or COS_CONFIG_PATH" }
  250. if (-not $env:COS_SECRET_KEY) { throw "Missing COS_SECRET_KEY or COS_CONFIG_PATH" }
  251. if (-not $env:COS_REGION) { throw "Missing COS_REGION" }
  252. $CoscliOptions += @(
  253. "--init-skip=true",
  254. "-i", $env:COS_SECRET_ID,
  255. "-k", $env:COS_SECRET_KEY,
  256. "-e", "cos.$($env:COS_REGION).myqcloud.com"
  257. )
  258. if ($env:COS_TOKEN) {
  259. $CoscliOptions += @("--token", $env:COS_TOKEN)
  260. }
  261. }
  262. $RunId = Get-Date -Format "yyyyMMddHHmmss"
  263. $CosSignExpire = $(if ($env:COS_SIGN_EXPIRE) { $env:COS_SIGN_EXPIRE } else { "604800" })
  264. $DefaultPrefix = "deploy/$DeployEnv/runtime/$RunId"
  265. $CosPrefix = $(if ($env:COS_PREFIX) { $env:COS_PREFIX } else { $DefaultPrefix }).Trim("/")
  266. $PackageBase = Split-Path -Leaf $PackageArchive
  267. $CosKey = $(if ($env:COS_KEY) { $env:COS_KEY } else { "$CosPrefix/$PackageBase" }).TrimStart("/")
  268. $CosUri = "cos://$($env:COS_BUCKET)/$CosKey"
  269. $Sha256 = (Get-FileHash $PackageArchive -Algorithm SHA256).Hash.ToLowerInvariant()
  270. Write-Log "Upload package to COS: $CosUri"
  271. & $CoscliBin @(@("cp", $PackageArchive, $CosUri) + $CoscliOptions)
  272. if ($LASTEXITCODE -ne 0) {
  273. throw "coscli cp failed, exit code: $LASTEXITCODE"
  274. }
  275. Write-Log "Generate signed URL, expire seconds: $CosSignExpire"
  276. $SignedOutput = & $CoscliBin @(@("signurl", $CosUri, "--time", $CosSignExpire, "--simple-output") + $CoscliOptions)
  277. if ($LASTEXITCODE -ne 0) {
  278. throw "coscli signurl failed, exit code: $LASTEXITCODE"
  279. }
  280. $SignedUrl = ($SignedOutput | Select-Object -Last 1).ToString()
  281. Write-Host ""
  282. Write-Host "Package: $PackageArchive"
  283. Write-Host "COS Key: $CosKey"
  284. Write-Host "SHA256: $Sha256"
  285. Write-Host "URL: $SignedUrl"
  286. Write-Host ""
  287. Write-Host "Target deploy command:"
  288. Write-Host " DEPLOY_ENV=$DeployEnv PACKAGE_SHA256=$Sha256 bash deploy-from-url.sh `"$SignedUrl`""
  289. Write-Host ""
  290. }
  291. Add-KnownToolPaths
  292. Assert-Command "docker"
  293. Assert-DockerDaemon
  294. Assert-Command "tar"
  295. $RootDir = Resolve-EmpRoot
  296. Write-Log "EMP root: $RootDir"
  297. Write-Log "Deploy root: $ScriptDir"
  298. Write-Log "Deploy env: $DeployEnv"
  299. if (-not $SkipBuild) {
  300. Assert-Command "mvn"
  301. Assert-Command "pnpm"
  302. Build-JavaImages
  303. Build-WsImage
  304. Build-AdminImage
  305. } else {
  306. Write-Log "Skip build, package existing local images only"
  307. }
  308. Prepare-Package
  309. Save-Images
  310. Archive-Package
  311. Publish-Package