no message

This commit is contained in:
hajimi
2026-06-12 22:23:47 +08:00
parent 8dacaea803
commit 7381097582
4 changed files with 338 additions and 62 deletions
+36 -10
View File
@@ -137,17 +137,23 @@ cd D:\www\sport-era
powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -RemoteDir /www/wwwroot/test-server.sbnews.net powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -RemoteDir /www/wwwroot/test-server.sbnews.net
``` ```
全量模式默认只打包以下目录: 全量模式默认以仓库根目录为源,只打包以下三个子目录:
```text ```text
server/app server/
server/config uniapp/
server/extend admin/
server/public/dongqiudi-crawler
server/route
``` ```
默认会排除运行态或较大目录,例如 `.env``vendor``runtime``public/uploads``public/admin``public/mobile`、爬虫 `data/logs/venv`。如确实需要包含,可追加参数 也就是说,远端 `RemoteDir` 下最终会形成
```text
<RemoteDir>/server
<RemoteDir>/uniapp
<RemoteDir>/admin
```
默认会排除运行态或较大目录,例如 `server/.env``server/vendor``server/runtime``server/public/uploads``server/public/admin``server/public/mobile`、爬虫 `data/logs/venv`,以及 `uniapp/node_modules``uniapp/dist``admin/node_modules``admin/dist`。如确实需要包含,可追加参数:
```bash ```bash
--include-env --include-env
@@ -197,11 +203,11 @@ cd D:\www\sport-era
powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -AutoIncremental -RemoteDir /www/wwwroot/test-server.sbnews.net -DryRun powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -AutoIncremental -RemoteDir /www/wwwroot/test-server.sbnews.net -DryRun
``` ```
脚本会在打包和上传前先检查 `server` 工作区;如果 `server` 内存在未提交、删除或未跟踪文件,会先自动提交这些变更,再继续计算增量范围。可以通过 `-AutoCommitMessage "提交说明"` 自定义这次自动提交的说明。若存在合并冲突,需要先手动解决。 脚本会在打包和上传前先检查默认打包范围(`server` + `uniapp` + `admin`);如果这些目录内存在未提交、删除或未跟踪文件,会先自动提交这些变更,再继续计算增量范围。可以通过 `-AutoCommitMessage "提交说明"` 自定义这次自动提交的说明。若存在合并冲突,需要先手动解决。
注意:自动增量只包含指定提交范围内已经提交到 Git 的差异,不会包含工作区未提交文件。需要指定任意起止提交时,继续使用 `-From <old_commit> -To <new_commit>` 注意:自动增量只包含指定提交范围内已经提交到 Git 的差异,不会包含工作区未提交文件。需要指定任意起止提交时,继续使用 `-From <old_commit> -To <new_commit>`
如果当前根目录 Git 只把 `server` 记录为 gitlink/submodule,且本机没有 `server/.git` 子仓库元数据,脚本无法拿到 `server` 内部的文件级差异;此时会自动降级为打包当前工作区默认的 5 个 server 目录,保证可以继续发布。 如果当前根目录 Git 只把 `server``uniapp``admin` 记录为 gitlink/submodule,脚本无法拿到对应目录内部的文件级差异;此时会把该目录从当前工作区按源码目录补进增量包,保证可以继续发布。
### 只演练不上传 ### 只演练不上传
@@ -225,14 +231,34 @@ cd D:\www\sport-era
powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -RemoteDir /www/wwwroot/test-server.sbnews.net -PostInstall "php think optimize:clear" powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -Sudo -RemoteDir /www/wwwroot/test-server.sbnews.net -PostInstall "php think optimize:clear"
``` ```
### 发布 uniapp 源码到服务器并在服务器构建
默认发布命令会把 `server/``uniapp/``admin/` 一起同步到远端根目录;如果需要在服务器端构建 `uniapp`,建议在远端根目录执行构建,再把 H5 产物写回同一套代码下的 `server/public/mobile`
```powershell
cd D:\www\sport-era
powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 `
-Sudo `
-RemoteDir /www/wwwroot/test-server.sbnews.net `
-UniappReleasePath ../server/public/mobile `
-PostInstall "cd uniapp && npm install && npm run build:h5"
```
说明:
- `-RemoteDir` 指向服务器上的项目根目录,解压后里面会同时有 `server/``uniapp/``admin/`
- `-UniappReleasePath` 会通过环境变量 `UNIAPP_RELEASE_PATH` 传给 `uniapp/scripts/release.mjs`;这里传 `../server/public/mobile`,是因为构建命令会先 `cd uniapp`,再把 H5 产物回写到兄弟目录 `server/public/mobile`
- 服务器需要预先安装 `node >= 18.19.0``npm`,否则 `npm install` / `npm run build:h5` 无法执行。
### 常见问题 ### 常见问题
1. `ssh: Could not resolve hostname sbnews`:检查 Windows 的 `C:\Users\Administrator\.ssh\config` 是否存在 `Host sbnews`,或直接用 `-HostName <服务器IP>` 1. `ssh: Could not resolve hostname sbnews`:检查 Windows 的 `C:\Users\Administrator\.ssh\config` 是否存在 `Host sbnews`,或直接用 `-HostName <服务器IP>`
2. PowerShell 禁止执行脚本:使用示例中的 `-ExecutionPolicy Bypass -File`,或以管理员身份调整当前用户执行策略。 2. PowerShell 禁止执行脚本:使用示例中的 `-ExecutionPolicy Bypass -File`,或以管理员身份调整当前用户执行策略。
3. `tar: Cannot utime``Cannot change mode`:远端目录权限不足,请加 `-Sudo`。脚本会使用 `sudo tar` 覆盖解压,并默认跳过还原 owner/permission,减少权限冲突。 3. `tar: Cannot utime``Cannot change mode`:远端目录权限不足,请加 `-Sudo`。脚本会使用 `sudo tar` 覆盖解压,并默认跳过还原 owner/permission,减少权限冲突。
4. 增量发布提示 commit 无效:确认 commit id 属于当前根目录 Git 可识别的历史。 4. 增量发布提示 commit 无效:确认 commit id 属于当前根目录 Git 可识别的历史。
5. `server is tracked as a root gitlink`:脚本检测到根 Git 只记录了 `server` 指针,无法做文件级增量,会自动改为打包当前工作区默认 5 个 server 目录 5. `root gitlink include paths will be copied from working tree`:脚本检测到默认发布范围里仍有 gitlink/submodule 目录,会把这些目录从当前工作区源码补进增量包
6. 仍需使用 Bash 兼容版时,可继续运行 `bash ./scripts/deploy-server.sh ...` 6. 仍需使用 Bash 兼容版时,可继续运行 `bash ./scripts/deploy-server.sh ...`
7. 服务器运行 `uniapp``node: command not found``npm: command not found`:先在服务器安装 Node.js 18.19+ 与 npm,再执行 `-SourceDir uniapp` 的发布命令。
<a href="https://www.likeshop.cn">![likeshop全开源商城](https://server.likeshop.cn/uploads/gitad/fastbuildAI.png)</a><br> <a href="https://www.likeshop.cn">![likeshop全开源商城](https://server.likeshop.cn/uploads/gitad/fastbuildAI.png)</a><br>
<a href="https://www.likeshop.cn">![likeshop全开源商城](https://server.likeshop.cn/uploads/gitad/likeshop.png)</a><br> <a href="https://www.likeshop.cn">![likeshop全开源商城](https://server.likeshop.cn/uploads/gitad/likeshop.png)</a><br>
+14
View File
@@ -0,0 +1,14 @@
INSERT INTO `la_system_menu` (`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`)
SELECT COALESCE((SELECT `pid` FROM `la_system_menu` WHERE `perms` = 'community.communityComment/lists' LIMIT 1), 200), 'C', '评论账号', '', 0, 'community.commentAccount/lists', 'comment_account', 'community/account/index', '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()
WHERE NOT EXISTS (
SELECT 1 FROM `la_system_menu`
WHERE `perms` = 'community.commentAccount/lists'
);
INSERT INTO `la_system_role_menu` (`role_id`, `menu_id`)
SELECT 1, (SELECT id FROM `la_system_menu` WHERE `perms` = 'community.commentAccount/lists' LIMIT 1)
WHERE NOT EXISTS (
SELECT 1 FROM `la_system_role_menu` rm
JOIN `la_system_menu` m ON m.id = rm.menu_id
WHERE m.perms = 'community.commentAccount/lists'
);
+212 -52
View File
@@ -1,8 +1,9 @@
[CmdletBinding()] [CmdletBinding()]
param( param(
[string]$SourceDir = "server", [string]$SourceDir = ".",
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[string]$RemoteDir, [string]$RemoteDir,
[string]$UniappReleasePath = "",
[string]$HostName = "sbnews", [string]$HostName = "sbnews",
[string]$SshBin = "ssh.exe", [string]$SshBin = "ssh.exe",
[switch]$AutoIncremental, [switch]$AutoIncremental,
@@ -181,7 +182,9 @@ function Start-ProcessWithInputText {
$inputFile = [System.IO.Path]::GetTempFileName() $inputFile = [System.IO.Path]::GetTempFileName()
try { try {
[System.IO.File]::WriteAllText($inputFile, $InputText, $Utf8NoBom) # Remote shells like `bash -s` choke on Windows CRLF in stdin scripts.
$normalizedInputText = $InputText.Replace("`r`n", "`n").Replace("`r", "`n")
[System.IO.File]::WriteAllText($inputFile, $normalizedInputText, $Utf8NoBom)
Start-ProcessWithInputFile -FilePath $FilePath -Arguments $Arguments -InputFile $inputFile Start-ProcessWithInputFile -FilePath $FilePath -Arguments $Arguments -InputFile $inputFile
} finally { } finally {
Remove-Item -LiteralPath $inputFile -Force -ErrorAction SilentlyContinue Remove-Item -LiteralPath $inputFile -Force -ErrorAction SilentlyContinue
@@ -195,13 +198,123 @@ function Remove-IfExists {
} }
} }
function Test-IsRepoRootDeploy {
return ($SourceDir -eq "." -or [string]::IsNullOrWhiteSpace($SourceDir))
}
function Get-SourceLabel {
if (Test-IsRepoRootDeploy) {
return "repo-root"
}
return $SourceDir
}
function Get-DeployPathspec { function Get-DeployPathspec {
if (Test-IsRepoRootDeploy) {
return @($FullIncludePaths)
}
if ([string]::IsNullOrEmpty($GitPathPrefix)) { if ([string]::IsNullOrEmpty($GitPathPrefix)) {
return @(".") return @(".")
} }
return @($GitPathPrefix) return @($GitPathPrefix)
} }
function Get-FullIncludePaths {
switch ($SourceDir.ToLowerInvariant()) {
"." {
return @("server", "uniapp", "admin", "docker")
}
"server" {
return @("app", "config", "extend", "public", "route")
}
"uniapp" {
return @(".")
}
default {
return @(".")
}
}
}
function Get-ArchiveExcludePatterns {
$patterns = @(
".git",
".idea",
".vscode",
".tmp",
".hbuilderx",
".gitignore",
"*.log",
"release-*.zip",
"*.zip",
"__pycache__",
"*.pyc",
"node_modules",
"dist"
)
switch ($SourceDir.ToLowerInvariant()) {
"." {
$patterns += @("uniapp/node_modules", "uniapp/dist", "admin/node_modules", "admin/dist")
if (-not $IncludeEnv) {
$patterns += "server/.env"
}
if (-not $IncludeVendor) {
$patterns += "server/vendor"
}
if (-not $IncludeRuntime) {
$patterns += "server/runtime"
}
if (-not $IncludeUploads) {
$patterns += "server/public/uploads"
}
if (-not $IncludePublicBuilds) {
$patterns += @("server/public/admin", "server/public/mobile")
}
if (-not $IncludeCrawlerData) {
$patterns += @(
"server/public/dongqiudi-crawler/data",
"server/public/dongqiudi-crawler/logs",
"server/public/dongqiudi-crawler/venv",
"server/public/dongqiudi-crawler/__pycache__"
)
}
}
"server" {
if (-not $IncludeEnv) {
$patterns += ".env"
}
if (-not $IncludeVendor) {
$patterns += "vendor"
}
if (-not $IncludeRuntime) {
$patterns += "runtime"
}
if (-not $IncludeUploads) {
$patterns += "public/uploads"
}
if (-not $IncludePublicBuilds) {
$patterns += @("public/admin", "public/mobile")
}
if (-not $IncludeCrawlerData) {
$patterns += @(
"public/dongqiudi-crawler/data",
"public/dongqiudi-crawler/logs",
"public/dongqiudi-crawler/venv",
"public/dongqiudi-crawler/__pycache__"
)
}
}
"uniapp" {
if (-not $IncludeEnv) {
$patterns += ".env"
}
}
}
return @($patterns)
}
function Commit-SourceChangesIfNeeded { function Commit-SourceChangesIfNeeded {
param( param(
[string]$Repository, [string]$Repository,
@@ -220,12 +333,12 @@ function Commit-SourceChangesIfNeeded {
$unmerged = @($statusLines | Where-Object { $_ -match "^(DD|AU|UD|UA|DU|AA|UU)" }) $unmerged = @($statusLines | Where-Object { $_ -match "^(DD|AU|UD|UA|DU|AA|UU)" })
if ($unmerged.Count -gt 0) { if ($unmerged.Count -gt 0) {
Write-DeployLog "unmerged changes detected under ${SourceDir}:" Write-DeployLog "unmerged changes detected under $(Get-SourceLabel):"
$unmerged | Select-Object -First 20 | ForEach-Object { Write-Host " $_" } $unmerged | Select-Object -First 20 | ForEach-Object { Write-Host " $_" }
Fail "resolve merge conflicts under $SourceDir before deploy" Fail "resolve merge conflicts under $(Get-SourceLabel) before deploy"
} }
Write-DeployLog "uncommitted changes detected under $SourceDir; auto committing before deploy:" Write-DeployLog "uncommitted changes detected under $(Get-SourceLabel); auto committing before deploy:"
$maxDisplay = 80 $maxDisplay = 80
$statusLines | Select-Object -First $maxDisplay | ForEach-Object { Write-Host " $_" } $statusLines | Select-Object -First $maxDisplay | ForEach-Object { Write-Host " $_" }
if ($statusLines.Count -gt $maxDisplay) { if ($statusLines.Count -gt $maxDisplay) {
@@ -234,9 +347,18 @@ function Commit-SourceChangesIfNeeded {
Invoke-Native "git" (@("-C", $Repository, "add", "-A", "--") + $Pathspec) Invoke-Native "git" (@("-C", $Repository, "add", "-A", "--") + $Pathspec)
& git -C $Repository diff --cached --quiet -- $Pathspec
if ($LASTEXITCODE -eq 0) {
Write-DeployLog "no committable root changes detected after git add; continue with current working tree payload"
return
}
if ($LASTEXITCODE -ne 1) {
Fail "git diff --cached failed"
}
$commitMessage = $AutoCommitMessage $commitMessage = $AutoCommitMessage
if ([string]::IsNullOrWhiteSpace($commitMessage)) { if ([string]::IsNullOrWhiteSpace($commitMessage)) {
$commitMessage = "deploy: auto commit $SourceDir changes $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $commitMessage = "deploy: auto commit $(Get-SourceLabel) changes $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
} }
Invoke-Native "git" (@("-C", $Repository, "commit", "-m", $commitMessage, "--") + $Pathspec) Invoke-Native "git" (@("-C", $Repository, "commit", "-m", $commitMessage, "--") + $Pathspec)
@@ -244,55 +366,42 @@ function Commit-SourceChangesIfNeeded {
Write-DeployLog "auto commit created: $newCommit" Write-DeployLog "auto commit created: $newCommit"
} }
function Copy-FullPayload { function Copy-WorkingTreePayloadPaths {
Write-DeployLog "building full payload from current working tree: $SourceDir" param(
Write-DeployLog "full include paths: $($FullIncludePaths -join ' ')" [string[]]$IncludePaths,
[string]$Reason
)
if ($IncludePaths.Count -eq 0) {
return
}
Write-DeployLog $Reason
Write-DeployLog "working tree include paths: $($IncludePaths -join ' ')"
$payloadTar = Join-Path $WorkDir "payload-copy.tar" $payloadTar = Join-Path $WorkDir "payload-copy.tar"
$tarArgs = @( $tarArgs = @(
"-cf", $payloadTar, "-cf", $payloadTar,
"-C", $SourceAbs, "-C", $SourceAbs
"--exclude=.git",
"--exclude=.idea",
"--exclude=.vscode",
"--exclude=.tmp",
"--exclude=.gitignore",
"--exclude=*.log",
"--exclude=release-*.zip",
"--exclude=*.zip",
"--exclude=__pycache__",
"--exclude=*.pyc"
) )
if (-not $IncludeEnv) { foreach ($pattern in (Get-ArchiveExcludePatterns)) {
$tarArgs += "--exclude=.env" if (-not [string]::IsNullOrWhiteSpace($pattern)) {
} $tarArgs += "--exclude=$pattern"
if (-not $IncludeVendor) { }
$tarArgs += "--exclude=vendor"
}
if (-not $IncludeRuntime) {
$tarArgs += "--exclude=runtime"
}
if (-not $IncludeUploads) {
$tarArgs += "--exclude=public/uploads"
}
if (-not $IncludePublicBuilds) {
$tarArgs += @("--exclude=public/admin", "--exclude=public/mobile")
}
if (-not $IncludeCrawlerData) {
$tarArgs += @(
"--exclude=public/dongqiudi-crawler/data",
"--exclude=public/dongqiudi-crawler/logs",
"--exclude=public/dongqiudi-crawler/venv",
"--exclude=public/dongqiudi-crawler/__pycache__"
)
} }
$tarArgs += $FullIncludePaths $tarArgs += $IncludePaths
Invoke-Native "tar" $tarArgs Invoke-Native "tar" $tarArgs
Invoke-Native "tar" @("-xf", $payloadTar, "-C", $PayloadDir) Invoke-Native "tar" @("-xf", $payloadTar, "-C", $PayloadDir)
} }
function Copy-FullPayload {
Copy-WorkingTreePayloadPaths `
-IncludePaths $FullIncludePaths `
-Reason "building full payload from current working tree: $(Get-SourceLabel)"
}
function Strip-SourcePrefix { function Strip-SourcePrefix {
param([string]$Path) param([string]$Path)
if ([string]::IsNullOrEmpty($GitPathPrefix)) { if ([string]::IsNullOrEmpty($GitPathPrefix)) {
@@ -308,6 +417,9 @@ function Strip-SourcePrefix {
function Test-DeployIncludedPath { function Test-DeployIncludedPath {
param([string]$RelativePath) param([string]$RelativePath)
foreach ($include in $FullIncludePaths) { foreach ($include in $FullIncludePaths) {
if ($include -eq ".") {
return $true
}
if ($RelativePath -eq $include -or $RelativePath.StartsWith("$include/")) { if ($RelativePath -eq $include -or $RelativePath.StartsWith("$include/")) {
return $true return $true
} }
@@ -315,6 +427,25 @@ function Test-DeployIncludedPath {
return $false return $false
} }
function Get-RootGitLinkIncludePaths {
if (-not (Test-IsRepoRootDeploy)) {
return @()
}
$lsFiles = @(& git -C $RepoRoot ls-files -s -- $FullIncludePaths)
if ($LASTEXITCODE -ne 0) {
Fail "git ls-files failed"
}
return @(
$lsFiles |
Where-Object { $_ -match "^160000\s+\S+\s+\d+\t(.+)$" } |
ForEach-Object { $Matches[1] } |
Where-Object { Test-DeployIncludedPath $_ } |
Sort-Object -Unique
)
}
function Test-UnderSourceDir { function Test-UnderSourceDir {
param([string]$GitPath) param([string]$GitPath)
if ([string]::IsNullOrEmpty($GitPathPrefix)) { if ([string]::IsNullOrEmpty($GitPathPrefix)) {
@@ -401,6 +532,11 @@ function Build-IncrementalPayload {
return return
} }
$rootGitLinkPayloadPaths = @()
if (Test-IsRepoRootDeploy) {
$rootGitLinkPayloadPaths = @($RootGitLinkIncludePaths)
}
$diffArgs = @("-C", $GitCwd, "diff", "--name-status", "--find-renames", $FromCommit, $ToCommit) $diffArgs = @("-C", $GitCwd, "diff", "--name-status", "--find-renames", $FromCommit, $ToCommit)
if (-not [string]::IsNullOrEmpty($GitPathPrefix)) { if (-not [string]::IsNullOrEmpty($GitPathPrefix)) {
$diffArgs += @("--", $GitPathPrefix) $diffArgs += @("--", $GitPathPrefix)
@@ -448,11 +584,15 @@ function Build-IncrementalPayload {
} }
} }
$script:ChangedDisplayPaths = @($script:ChangedDisplayPaths | Sort-Object -Unique)
$script:DeletedDisplayPaths = @($script:DeletedDisplayPaths | Sort-Object -Unique) $script:DeletedDisplayPaths = @($script:DeletedDisplayPaths | Sort-Object -Unique)
$changedGitPaths = @($changedGitPaths | Sort-Object -Unique) $changedGitPaths = @($changedGitPaths | Sort-Object -Unique)
if ($changedGitPaths.Count -eq 0 -and $script:DeletedDisplayPaths.Count -eq 0) { if ($rootGitLinkPayloadPaths.Count -gt 0) {
$script:ChangedDisplayPaths += @($rootGitLinkPayloadPaths | ForEach-Object { "$_/" })
}
$script:ChangedDisplayPaths = @($script:ChangedDisplayPaths | Sort-Object -Unique)
if ($changedGitPaths.Count -eq 0 -and $script:DeletedDisplayPaths.Count -eq 0 -and $rootGitLinkPayloadPaths.Count -eq 0) {
Write-DeployLog "no changed files detected between $FromCommit and $ToCommit under $SourceDir" Write-DeployLog "no changed files detected between $FromCommit and $ToCommit under $SourceDir"
$script:NoChanges = $true $script:NoChanges = $true
return return
@@ -477,11 +617,18 @@ function Build-IncrementalPayload {
Get-ChildItem -LiteralPath $extractSource -Force | Copy-Item -Destination $PayloadDir -Recurse -Force Get-ChildItem -LiteralPath $extractSource -Force | Copy-Item -Destination $PayloadDir -Recurse -Force
} }
} }
if ($rootGitLinkPayloadPaths.Count -gt 0) {
Copy-WorkingTreePayloadPaths `
-IncludePaths $rootGitLinkPayloadPaths `
-Reason "building working tree payload for root gitlink include paths"
}
} }
$SourceDir = Normalize-DeployDir $SourceDir $SourceDir = Normalize-DeployDir $SourceDir
$RemoteDir = Normalize-DeployDir $RemoteDir $RemoteDir = Normalize-DeployDir $RemoteDir
$RemoteTmp = Normalize-DeployDir $RemoteTmp $RemoteTmp = Normalize-DeployDir $RemoteTmp
$SourceLabel = Get-SourceLabel
if ($AutoIncremental -and ($From -or $To)) { if ($AutoIncremental -and ($From -or $To)) {
Fail "-AutoIncremental cannot be used with -From/-To" Fail "-AutoIncremental cannot be used with -From/-To"
@@ -519,7 +666,7 @@ if (-not (Test-Path -LiteralPath $SourceAbs -PathType Container)) {
Fail "source directory not found: $SourceDir" Fail "source directory not found: $SourceDir"
} }
$FullIncludePaths = @("app", "config", "extend", "public", "route") $FullIncludePaths = @(Get-FullIncludePaths)
$GitCwd = $RepoRoot $GitCwd = $RepoRoot
$GitPathPrefix = $SourceDir $GitPathPrefix = $SourceDir
$SourceIsGitRepo = $false $SourceIsGitRepo = $false
@@ -543,6 +690,10 @@ if (-not $SourceIsGitRepo -and ($lsFiles | Where-Object { $_ -match "^160000\s"
} }
$DeployPathspec = @(Get-DeployPathspec) $DeployPathspec = @(Get-DeployPathspec)
$RootGitLinkIncludePaths = @(Get-RootGitLinkIncludePaths)
if ($RootGitLinkIncludePaths.Count -gt 0) {
Write-DeployLog "root gitlink include paths will be copied from working tree: $($RootGitLinkIncludePaths -join ' ')"
}
Commit-SourceChangesIfNeeded -Repository $GitCwd -Pathspec $DeployPathspec Commit-SourceChangesIfNeeded -Repository $GitCwd -Pathspec $DeployPathspec
if ($Mode -eq "incremental") { if ($Mode -eq "incremental") {
@@ -564,7 +715,7 @@ $WorkDir = Join-Path $TempRoot ("deploy-server-" + [System.Guid]::NewGuid().ToSt
$PackageRoot = Join-Path $WorkDir "package" $PackageRoot = Join-Path $WorkDir "package"
$PayloadDir = Join-Path $PackageRoot "payload" $PayloadDir = Join-Path $PackageRoot "payload"
$ExtractDir = Join-Path $WorkDir "extract" $ExtractDir = Join-Path $WorkDir "extract"
$ArchivePath = Join-Path $WorkDir ("{0}-{1}.tar.gz" -f ($SourceDir -replace "[\\/]", "-"), $Mode) $ArchivePath = Join-Path $WorkDir ("{0}-{1}.tar.gz" -f $SourceLabel, $Mode)
$DeletedPathsFile = Join-Path $PackageRoot ".deleted-paths.txt" $DeletedPathsFile = Join-Path $PackageRoot ".deleted-paths.txt"
$DeployInfoFile = Join-Path $PackageRoot ".deploy-info.txt" $DeployInfoFile = Join-Path $PackageRoot ".deploy-info.txt"
$script:ChangedDisplayPaths = @() $script:ChangedDisplayPaths = @()
@@ -588,9 +739,10 @@ try {
$deployInfo = @( $deployInfo = @(
"mode=$Mode", "mode=$Mode",
"source_dir=$SourceDir", "source_dir=$SourceLabel",
"host=$HostName", "host=$HostName",
"remote_dir=$RemoteDir", "remote_dir=$RemoteDir",
"uniapp_release_path=$UniappReleasePath",
"built_at=$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" "built_at=$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
) )
if ($Mode -eq "incremental") { if ($Mode -eq "incremental") {
@@ -619,8 +771,8 @@ try {
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$pidValue = $PID $pidValue = $PID
$remoteWorkDir = "$RemoteTmp/$($SourceDir -replace '[\\/]', '-')-deploy-$timestamp-$pidValue" $remoteWorkDir = "$RemoteTmp/$SourceLabel-deploy-$timestamp-$pidValue"
$remoteArchive = "$RemoteTmp/$($SourceDir -replace '[\\/]', '-')-$Mode-$timestamp.tar.gz" $remoteArchive = "$RemoteTmp/$SourceLabel-$Mode-$timestamp.tar.gz"
$remoteTmpQ = ConvertTo-RemoteSingleQuoted $RemoteTmp $remoteTmpQ = ConvertTo-RemoteSingleQuoted $RemoteTmp
$remoteArchiveQ = ConvertTo-RemoteSingleQuoted $remoteArchive $remoteArchiveQ = ConvertTo-RemoteSingleQuoted $remoteArchive
@@ -640,6 +792,7 @@ try {
"REMOTE_DIR=$(ConvertTo-RemoteSingleQuoted $RemoteDir)", "REMOTE_DIR=$(ConvertTo-RemoteSingleQuoted $RemoteDir)",
"REMOTE_ARCHIVE=$(ConvertTo-RemoteSingleQuoted $remoteArchive)", "REMOTE_ARCHIVE=$(ConvertTo-RemoteSingleQuoted $remoteArchive)",
"REMOTE_WORK_DIR=$(ConvertTo-RemoteSingleQuoted $remoteWorkDir)", "REMOTE_WORK_DIR=$(ConvertTo-RemoteSingleQuoted $remoteWorkDir)",
"UNIAPP_RELEASE_PATH=$(ConvertTo-RemoteSingleQuoted $UniappReleasePath)",
"KEEP_REMOTE_TEMP='$keepRemoteTempValue'", "KEEP_REMOTE_TEMP='$keepRemoteTempValue'",
"POST_INSTALL_B64='$postInstallB64'", "POST_INSTALL_B64='$postInstallB64'",
"USE_SUDO='$useSudoValue'", "USE_SUDO='$useSudoValue'",
@@ -762,8 +915,15 @@ if [[ -n "${POST_INSTALL_B64:-}" ]]; then
remote_log "run post-install command" remote_log "run post-install command"
POST_INSTALL_CMD="$(printf '%s' "$POST_INSTALL_B64" | base64 -d)" POST_INSTALL_CMD="$(printf '%s' "$POST_INSTALL_B64" | base64 -d)"
cd "$REMOTE_DIR" cd "$REMOTE_DIR"
if [[ -n "${UNIAPP_RELEASE_PATH:-}" ]]; then
export UNIAPP_RELEASE_PATH
fi
if [[ "${USE_SUDO:-0}" == "1" ]]; then if [[ "${USE_SUDO:-0}" == "1" ]]; then
sudo bash -lc "$POST_INSTALL_CMD" if [[ -n "${UNIAPP_RELEASE_PATH:-}" ]]; then
sudo env UNIAPP_RELEASE_PATH="$UNIAPP_RELEASE_PATH" bash -lc "$POST_INSTALL_CMD"
else
sudo bash -lc "$POST_INSTALL_CMD"
fi
else else
bash -lc "$POST_INSTALL_CMD" bash -lc "$POST_INSTALL_CMD"
fi fi
@@ -782,7 +942,7 @@ remote_log "remote install finished"
Write-DeployLog "deploy finished" Write-DeployLog "deploy finished"
Write-DeployLog "mode: $Mode" Write-DeployLog "mode: $Mode"
Write-DeployLog "source: $SourceDir" Write-DeployLog "source: $SourceLabel"
Write-DeployLog "remote: ${HostName}:$RemoteDir" Write-DeployLog "remote: ${HostName}:$RemoteDir"
} finally { } finally {
if ($WorkDir -and (Test-Path -LiteralPath $WorkDir)) { if ($WorkDir -and (Test-Path -LiteralPath $WorkDir)) {
+76
View File
@@ -13,6 +13,81 @@
## 一、已完成事项 ## 一、已完成事项
### 68. 默认部署切换为根目录同时上传 server、uniapp 与 admin
- 状态:已完成 - 时间:2026-06-12
完成内容:
- `scripts/deploy-server.ps1` 默认 `SourceDir` 改为仓库根目录 `.`,不再先发 `server` 再补发前端目录,而是直接以根目录为源打一个包。
- 根目录默认只包含 `server/``uniapp/``admin/` 三个子目录,因此远端 `RemoteDir` 下会直接形成三套源码目录,方便后续按 `server/public``uniapp``admin` 分别配置 Nginx 和构建流程。
- 根目录模式下,排除规则已同步切到子目录路径:会继续默认排除 `server/.env``server/vendor``server/runtime``server/public/uploads``server/public/admin``server/public/mobile`、crawler 大文件目录,以及 `uniapp/node_modules``uniapp/dist``admin/node_modules``admin/dist`
- 默认增量发布也切到根目录范围,`-AutoIncremental` / `-AutoIncrementalCommits` / `-From` / `-To` 会同时跟踪 `server``uniapp``admin` 的 Git 变更和删除文件。
- 若根仓库里某个默认目录仍是 gitlink/submodule 记录,脚本会把该目录从当前工作区按源码目录补进增量包,避免 `admin` 这类目录在转换期间漏传。
- `README.md` 已同步更新根目录发布、远端目录结构以及在远端根目录内执行 `cd uniapp && npm install && npm run build:h5` 的示例。
涉及模块:
- `scripts/deploy-server.ps1`
- `README.md`
验证记录:
- 已在临时最小 Git 仓库执行默认 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -DryRun -RemoteDir /tmp/test`,修改前日志只出现 `server` 打包,确认红灯。
- 修改后已在临时最小 Git 仓库执行默认 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -DryRun -AutoIncremental -AutoIncrementalCommits 3 -RemoteDir /tmp/test`,日志已同时出现 `server/app/ok.txt``uniapp/src/App.vue``admin/src/main.ts` 的增量变更,确认根目录模式能同时覆盖三套源码。
- 已补跑 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -SourceDir server -DryRun -RemoteDir /tmp/test`,确认显式单独发布 `server` 的老用法仍可继续使用。
- 已在真实仓库执行 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -DryRun -AutoIncremental -AutoIncrementalCommits 3 -RemoteDir /www/wwwroot/test-server.sbnews.net`,日志出现 `root gitlink include paths will be copied from working tree: admin``+ admin/`,确认 `admin` 仍是 gitlink 记录时也会被补进增量包。
- 已在真实仓库执行默认全量 DryRun,日志显示 `working tree include paths: server uniapp admin`,确认全量包也会同时包含三套源码。
### 67. 部署脚本支持发布 uniapp 源码到服务器
- 状态:已完成 - 时间:2026-06-12
完成内容:
- `scripts/deploy-server.ps1` 现在支持 `-SourceDir uniapp`,可将 `uniapp` 源码按独立目录发布到服务器,不再只支持 `server` 后端目录。
- `uniapp` 全量打包默认改为包含整个源码目录,并排除 `node_modules``dist``.hbuilderx` 等不应上传的本地构建/IDE 目录;增量发布也可正确识别 `uniapp` 目录下的 Git 变更与删除文件。
- 部署脚本新增 `-UniappReleasePath`,会在远端执行 `-PostInstall` 时通过环境变量 `UNIAPP_RELEASE_PATH` 传给 `uniapp/scripts/release.mjs`,让服务器构建 `npm run build:h5` 后可把 H5 产物直接写入指定站点的 `public/mobile`
- `uniapp/scripts/release.mjs` 新增 `UNIAPP_RELEASE_PATH` 覆盖能力,本地构建仍默认输出到 `../server/public/mobile`,服务端构建时则可改写到测试站真实目录。
- `README.md` 已补充“发布 uniapp 源码到服务器并在服务器构建”的标准命令示例与 Node.js 版本要求。
涉及模块:
- `scripts/deploy-server.ps1`
- `uniapp/scripts/release.mjs`
- `README.md`
验证记录:
- 已先执行 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -SourceDir uniapp -RemoteDir /tmp/sport-era-uniapp -DryRun`,修改前失败,确认原脚本不支持 `uniapp` 目录打包。
- 修改后已执行 `powershell -ExecutionPolicy Bypass -File .\scripts\deploy-server.ps1 -SourceDir uniapp -RemoteDir /tmp/sport-era-uniapp -DryRun`,全量打包演练通过。
- 已执行 `git diff --check -- scripts/deploy-server.ps1 uniapp/scripts/release.mjs README.md 业务进度管理.md`,未发现空白错误。
- 已通过 SSH 检查测试服当前缺少 `node` / `npm`,因此本次仅验证到“源码可发布 + 远端构建命令已就绪”,尚未在测试服实际执行 `npm install` / `npm run build:h5`
### 66. 管理端评论账号列表与统一评论列表
- 状态:已完成 - 时间:2026-06-12
完成内容:
- 管理端新增“评论账号”后台契约与页面,后端 `community.commentAccount/lists` 仅筛选 `ai_comment_*` 虚拟评论账号,返回账号、昵称、人设标签、禁用状态、资讯评论数、社区评论数和总评论数。
- 现有“社区评论管理”升级为统一评论列表,不再只看社区帖子评论;现在同一页面可同时查看资讯评论与社区评论,并按评论类型、虚拟/真实评论、状态、目标 ID、关键词筛选。
- 统一评论列表后端通过合并 `la_article_comment``la_community_comment` 输出统一字段:`comment_type``source_type_desc``target_id``target_title``target_summary``target_owner_nickname``account``is_virtual``user_type_desc``status` 等。
- 评论审核与删除逻辑已扩展到两类评论:资讯评论操作 `la_article_comment.is_show``la_article.comment_count`,社区评论操作 `la_community_comment.status``la_community_post.comment_count`
- 新增菜单 SQL `docs/sql/insert_menu_comment_account.sql`,用于把“评论账号”挂到现有“社区管理”菜单下;统一评论列表继续复用原有 `community.communityComment/lists` 菜单入口。
涉及模块:
- `server/app/adminapi/controller/community/CommunityCommentController.php`
- `server/app/adminapi/lists/community/CommunityCommentLists.php`
- `server/app/adminapi/logic/community/CommunityCommentLogic.php`
- `server/app/adminapi/validate/community/CommunityCommentValidate.php`
- `server/app/adminapi/controller/community/CommentAccountController.php`
- `server/app/adminapi/lists/community/CommentAccountLists.php`
- `admin/src/api/community.ts`
- `admin/src/views/community/comment/index.vue`
- `admin/src/views/community/account/index.vue`
- `docs/sql/insert_menu_comment_account.sql`
- `qa/backend/test_comment_admin_static.php`
验证记录:
- 已先执行 `php qa/backend/test_comment_admin_static.php`,新增文件前失败,提示缺少 `admin/src/views/community/account/index.vue`
- 修改后已执行 `php qa/backend/test_comment_admin_static.php`,静态检查通过。
- 已执行 `php -l` 检查 `CommunityCommentController.php``CommunityCommentLogic.php``CommunityCommentLists.php``CommunityCommentValidate.php``CommentAccountController.php``CommentAccountLists.php`,语法检查通过。
- 已在 `admin` 目录执行 `npm run build`,构建通过并刷新 `server/public/admin` 产物。
### 65. Docker AI评论调度接入帖子与资讯 ### 65. Docker AI评论调度接入帖子与资讯
- 状态:已完成 - 时间:2026-06-12 - 状态:已完成 - 时间:2026-06-12
@@ -613,6 +688,7 @@
- 排查 `scripts/deploy-server.ps1` 全量部署测试服时报 `tar: Cannot change mode ... Operation not permitted`,确认 SSH 用户 `ubuntu` 仅通过 `www` 组拥有目标目录写权限,并不是目录 owner,GNU tar 覆盖解包目录项时会尝试 `chmod` 已有目录导致失败。 - 排查 `scripts/deploy-server.ps1` 全量部署测试服时报 `tar: Cannot change mode ... Operation not permitted`,确认 SSH 用户 `ubuntu` 仅通过 `www` 组拥有目标目录写权限,并不是目录 owner,GNU tar 覆盖解包目录项时会尝试 `chmod` 已有目录导致失败。
- 远端安装 payload 改为优先使用 `rsync -r --links --no-perms --no-owner --no-group`,不保留 owner/group/perms;远端无 rsync 时回退为 `find + mkdir/cp/ln` 逐文件复制,避免对已有目录执行 chmod。 - 远端安装 payload 改为优先使用 `rsync -r --links --no-perms --no-owner --no-group`,不保留 owner/group/perms;远端无 rsync 时回退为 `find + mkdir/cp/ln` 逐文件复制,避免对已有目录执行 chmod。
- 修复通过 `ssh bash -s` 下发远端 Bash 文本时可能写入 UTF-8 BOM 的问题,改为用 `UTF8 No BOM` 字节流写入 stdin,避免 `set -euo pipefail` 失效导致部署“假成功”。 - 修复通过 `ssh bash -s` 下发远端 Bash 文本时可能写入 UTF-8 BOM 的问题,改为用 `UTF8 No BOM` 字节流写入 stdin,避免 `set -euo pipefail` 失效导致部署“假成功”。
- 2026-06-12 再次排查测试服增量部署时报 `invalid option namepefail`,根因是远端 Bash 脚本 stdin 保留了 Windows `CRLF`,导致 `set -euo pipefail` 被识别成 `pipefail\r` 非法选项;现已在写入 stdin 前统一转换为 `LF`
- 保留当前全量部署包含整个 `public` 目录的变更,解决包含更多已有目录后触发的部署失败。 - 保留当前全量部署包含整个 `public` 目录的变更,解决包含更多已有目录后触发的部署失败。
涉及模块: 涉及模块: