From a496b1b878c7b69460042c19c746b25d6c5f2fe3 Mon Sep 17 00:00:00 2001 From: zeaslity Date: Wed, 27 May 2026 17:44:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=86=85=E5=AE=B9prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shell脚本}/shell-prompt-2.md | 0 .../shell脚本}/shell-prompt.md | 0 13-构建专家-SHELL/构建专家/build-release.ps1 | 420 ++++++++++++++++++ 13-构建专家-SHELL/构建专家/最终构建-prompt.md | 236 ++++++++++ 13-构建专家-SHELL/构建专家/构建内容.md | 14 + 5 files changed, 670 insertions(+) rename {13-SHELL脚本 => 13-构建专家-SHELL/shell脚本}/shell-prompt-2.md (100%) rename {13-SHELL脚本 => 13-构建专家-SHELL/shell脚本}/shell-prompt.md (100%) create mode 100644 13-构建专家-SHELL/构建专家/build-release.ps1 create mode 100644 13-构建专家-SHELL/构建专家/最终构建-prompt.md create mode 100644 13-构建专家-SHELL/构建专家/构建内容.md diff --git a/13-SHELL脚本/shell-prompt-2.md b/13-构建专家-SHELL/shell脚本/shell-prompt-2.md similarity index 100% rename from 13-SHELL脚本/shell-prompt-2.md rename to 13-构建专家-SHELL/shell脚本/shell-prompt-2.md diff --git a/13-SHELL脚本/shell-prompt.md b/13-构建专家-SHELL/shell脚本/shell-prompt.md similarity index 100% rename from 13-SHELL脚本/shell-prompt.md rename to 13-构建专家-SHELL/shell脚本/shell-prompt.md diff --git a/13-构建专家-SHELL/构建专家/build-release.ps1 b/13-构建专家-SHELL/构建专家/build-release.ps1 new file mode 100644 index 0000000..c4c95e6 --- /dev/null +++ b/13-构建专家-SHELL/构建专家/build-release.ps1 @@ -0,0 +1,420 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.5 + +[CmdletBinding()] +param( + [ValidateSet("sync", "build", "all", "clean")] + [string]$Action = "all", + + [ValidateSet("linux-x86_64", "linux-aarch64", "all")] + [string]$Target = "all", + + [ValidateSet("dev", "release")] + [string]$BuildProfile = "dev", + + [Alias("RunnableHostIp", "TargetHostIp")] + [string[]]$RuntimeHostIp = @(), + + [string]$OutputDir = "build/release", + + [Parameter(Mandatory = $true)] + [string]$LinuxHostUser, + + [Parameter(Mandatory = $true)] + [string]$LinuxHostIp, + + [Parameter(Mandatory = $true)] + [string]$LinuxRemoteWorkspaceDir, + + [Parameter(Mandatory = $true)] + [string]$WindowsSshKeyPath, + + [Parameter(Mandatory = $true)] + [string]$WindowsRsyncExe, + + [string[]]$RsyncExcludes = @( + ".git/", + ".idea/", + ".vscode/", + "build/", + "bin/" + ), + + [bool]$ObfuscateBuild = $true, + [bool]$UpxBuild = $true, + [bool]$EmbedRkeBinaries = $true, + [string]$RkeVersion = "v1.8.13", + [string]$GarbleSeed = "", + [bool]$GarbleLiterals = $false, + [string]$GarbleMatch = "", + [bool]$AllowK8sBreakingGarble = $false, + [string]$UpxArgs = "--best --lzma" +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" + +if ($BuildProfile -eq "release") { + throw "本地 PowerShell 构建禁止 release 模式。release 构建仅允许在受控 Runner 中执行,并由超级管理员凭据授权。" +} + +$ModuleName = "rmdc-watchdog" +$ModuleRoot = Split-Path -Parent $PSScriptRoot +$WorkspaceRoot = Split-Path -Parent $ModuleRoot + +function Write-Log { + param( + [Parameter(Mandatory = $true)][ValidateSet("INFO", "WARN", "SUCCESS")][string]$Level, + [Parameter(Mandatory = $true)][string]$Message + ) + $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Host "[$ts] [$Level] $Message" +} + +function Resolve-FullPath { + param([Parameter(Mandatory = $true)][string]$Path) + if (-not (Test-Path -LiteralPath $Path)) { + throw "路径不存在:$Path" + } + return (Resolve-Path -LiteralPath $Path).Path +} + +function Assert-AbsoluteWindowsPath { + param( + [Parameter(Mandatory = $true)][string]$Path, + [Parameter(Mandatory = $true)][string]$Name + ) + if (-not [System.IO.Path]::IsPathRooted($Path)) { + throw "$Name 必须是 Windows 绝对路径:$Path" + } +} + +function Assert-AbsoluteLinuxPath { + param( + [Parameter(Mandatory = $true)][string]$Path, + [Parameter(Mandatory = $true)][string]$Name + ) + if (-not $Path.StartsWith("/")) { + throw "$Name 必须是 Linux 绝对路径:$Path" + } +} + +function Assert-IPAddress { + param( + [Parameter(Mandatory = $true)][string]$Value, + [Parameter(Mandatory = $true)][string]$Name + ) + $parsed = [System.Net.IPAddress]::None + if (-not [System.Net.IPAddress]::TryParse($Value, [ref]$parsed)) { + throw "$Name 必须是有效 IP 地址:$Value" + } +} + +function Normalize-IPAddressList { + param( + [Parameter(Mandatory = $true)][string[]]$Values, + [Parameter(Mandatory = $true)][string]$Name + ) + $result = [System.Collections.Generic.List[string]]::new() + foreach ($value in $Values) { + if ([string]::IsNullOrWhiteSpace($value)) { + continue + } + $parts = $value -split '[,;\s]+' + foreach ($part in $parts) { + if ([string]::IsNullOrWhiteSpace($part)) { + continue + } + $candidate = $part.Trim() + Assert-IPAddress -Value $candidate -Name $Name + if (-not $result.Contains($candidate)) { + [void]$result.Add($candidate) + } + } + } + if ($result.Count -eq 0) { + throw "$Name 必须至少包含一个有效 IP 地址。" + } + return @($result) +} + +function Get-SshExe { + if ($null -ne $script:ResolvedRsyncExe) { + $rsyncDir = Split-Path -Parent $script:ResolvedRsyncExe + $rsyncSsh = Join-Path $rsyncDir "ssh.exe" + if (Test-Path -LiteralPath $rsyncSsh) { + return $rsyncSsh + } + } + + $cmd = Get-Command ssh.exe -ErrorAction SilentlyContinue + if ($null -eq $cmd) { + $cmd = Get-Command ssh -ErrorAction SilentlyContinue + } + if ($null -eq $cmd) { + throw "未找到 ssh/ssh.exe,请安装 OpenSSH 客户端或使用 cwRsync 自带 ssh.exe。" + } + return $cmd.Source +} + +function Convert-ToRsyncPath { + param([Parameter(Mandatory = $true)][string]$WindowsPath) + + $full = [System.IO.Path]::GetFullPath($WindowsPath) + if ($full -match '^[A-Za-z]:\\') { + $drive = $full.Substring(0, 1).ToLowerInvariant() + $rest = $full.Substring(2) -replace '\\', '/' + return "/cygdrive/$drive$rest" + } + if ($full.StartsWith("/")) { + return $full + } + throw "无法转换为 rsync 可识别路径:$WindowsPath" +} + +function Invoke-External { + param( + [Parameter(Mandatory = $true)][string]$Exe, + [Parameter(Mandatory = $true)][string[]]$Arguments, + [Parameter()][string]$StdinContent + ) + + Write-Log -Level INFO -Message ("执行命令: {0} {1}" -f $Exe, ($Arguments -join " ")) + + if ($PSBoundParameters.ContainsKey("StdinContent")) { + $tmp = [System.IO.Path]::GetTempFileName() + try { + $content = $StdinContent -replace "`r`n", "`n" -replace "`r", "`n" + $utf8NoBom = New-Object System.Text.UTF8Encoding($false) + [System.IO.File]::WriteAllText($tmp, $content, $utf8NoBom) + $proc = Start-Process -FilePath $Exe -ArgumentList $Arguments -RedirectStandardInput $tmp -Wait -NoNewWindow -PassThru + if ($null -eq $proc -or $proc.ExitCode -ne 0) { + $exitCode = if ($null -eq $proc) { -1 } else { $proc.ExitCode } + throw "命令执行失败,退出码:$exitCode" + } + } + finally { + Remove-Item -Path $tmp -Force -ErrorAction SilentlyContinue + } + return + } + + & $Exe @Arguments + if ($LASTEXITCODE -ne 0) { + throw "命令执行失败,退出码:$LASTEXITCODE" + } +} + +function New-RemoteShellScript { + param([Parameter(Mandatory = $true)][string]$Body) + @" +set -Eeuo pipefail + +log() { + printf '[%s] [REMOTE] %s\n' "`$(date '+%F %T')" "`$*" +} + +$Body +"@ +} + +function Invoke-RemoteBash { + param([Parameter(Mandatory = $true)][string]$ScriptContent) + $sshArgs = @( + "-i", $script:ResolvedSshKeyPath, + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "$script:LinuxHostUser@$script:LinuxHostIp", + "bash", "-s", "--" + ) + Invoke-External -Exe $script:SshExe -Arguments $sshArgs -StdinContent $ScriptContent +} + +function Convert-ToShellSingleQuoted { + param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Value) + return "'" + ($Value -replace "'", "'""'""'") + "'" +} + +function Get-LocalBranch { + param([Parameter(Mandatory = $true)][string]$RepoPath) + $branch = (git -C $RepoPath symbolic-ref --quiet --short HEAD 2>$null) + if ([string]::IsNullOrWhiteSpace($branch)) { + $branch = (git -C $RepoPath rev-parse --short HEAD 2>$null) + } + if ([string]::IsNullOrWhiteSpace($branch)) { + return "detached" + } + return $branch.Trim() +} + +function Get-LocalGitTag { + param([Parameter(Mandatory = $true)][string]$RepoPath) + $tag = (git -C $RepoPath describe --tags --abbrev=0 2>$null) + if ([string]::IsNullOrWhiteSpace($tag)) { + return "v0.0.0" + } + return $tag.Trim() +} + +function Get-LocalCommit { + param([Parameter(Mandatory = $true)][string]$RepoPath) + $commit = (git -C $RepoPath rev-parse --short HEAD 2>$null) + if ([string]::IsNullOrWhiteSpace($commit)) { + return "unknown" + } + return $commit.Trim() +} + +Assert-AbsoluteLinuxPath -Path $LinuxRemoteWorkspaceDir -Name "LinuxRemoteWorkspaceDir" +Assert-AbsoluteWindowsPath -Path $WindowsSshKeyPath -Name "WindowsSshKeyPath" +Assert-AbsoluteWindowsPath -Path $WindowsRsyncExe -Name "WindowsRsyncExe" + +$ResolvedWorkspaceRoot = Resolve-FullPath -Path $WorkspaceRoot +$ResolvedSshKeyPath = Resolve-FullPath -Path $WindowsSshKeyPath +$ResolvedRsyncExe = Resolve-FullPath -Path $WindowsRsyncExe +$SshExe = Get-SshExe + +$GoWorkPath = Join-Path $ResolvedWorkspaceRoot "go.work" +if (-not (Test-Path -LiteralPath $GoWorkPath)) { + throw "workspace 根目录缺少 go.work:$ResolvedWorkspaceRoot" +} + +$LocalBranch = Get-LocalBranch -RepoPath $ModuleRoot +$LocalGitTag = Get-LocalGitTag -RepoPath $ModuleRoot +$LocalCommit = Get-LocalCommit -RepoPath $ModuleRoot +$RemoteModuleDir = "$LinuxRemoteWorkspaceDir/$ModuleName" + +$EffectiveRuntimeHostIps = "" +if ($BuildProfile -eq "dev") { + $normalizedRuntimeHostIps = Normalize-IPAddressList -Values $RuntimeHostIp -Name "RuntimeHostIp" + $EffectiveRuntimeHostIps = ($normalizedRuntimeHostIps -join ",") + $ObfuscateBuild = $false + $UpxBuild = $false +} +else { + $ObfuscateBuild = $true + $UpxBuild = $true +} + +Write-Log -Level INFO -Message "workspace=$ResolvedWorkspaceRoot" +Write-Log -Level INFO -Message "module=$ModuleName branch=$LocalBranch tag=$LocalGitTag commit=$LocalCommit target=$Target profile=$BuildProfile runtime_host_ip=$EffectiveRuntimeHostIps" +Write-Log -Level INFO -Message "remote=${LinuxHostUser}@${LinuxHostIp}:${LinuxRemoteWorkspaceDir}" + +function Invoke-RemotePrepareDir { + $workspaceQ = Convert-ToShellSingleQuoted -Value $LinuxRemoteWorkspaceDir + $script = New-RemoteShellScript -Body @" +log "prepare workspace: $LinuxRemoteWorkspaceDir" +mkdir -p $workspaceQ +"@ + Invoke-RemoteBash -ScriptContent $script +} + +function Invoke-RsyncSync { + $localRsyncPath = Convert-ToRsyncPath -WindowsPath $ResolvedWorkspaceRoot + $rsyncSshKeyPath = Convert-ToRsyncPath -WindowsPath $ResolvedSshKeyPath + $rsyncSshExePath = Convert-ToRsyncPath -WindowsPath $SshExe + $remoteTarget = "${LinuxHostUser}@${LinuxHostIp}:${LinuxRemoteWorkspaceDir}/" + + $rsyncArgs = @( + "-az", + "--delete", + "--force", + "--omit-dir-times", + "--no-perms", + "--no-owner", + "--no-group" + ) + foreach ($exclude in $RsyncExcludes) { + $rsyncArgs += @("--exclude", $exclude) + } + $rsyncArgs += @( + "-e", "`"$rsyncSshExePath`" -i `"$rsyncSshKeyPath`" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null", + "$localRsyncPath/", + $remoteTarget + ) + + Invoke-External -Exe $ResolvedRsyncExe -Arguments $rsyncArgs +} + +function Invoke-RemoteClean { + $workspaceQ = Convert-ToShellSingleQuoted -Value $LinuxRemoteWorkspaceDir + $script = New-RemoteShellScript -Body @" +log "cleanup workspace: $LinuxRemoteWorkspaceDir" +rm -rf $workspaceQ +"@ + Invoke-RemoteBash -ScriptContent $script +} + +function Invoke-RemoteBuild { + $targetQ = Convert-ToShellSingleQuoted -Value $Target + $outputQ = Convert-ToShellSingleQuoted -Value $OutputDir + $moduleDirQ = Convert-ToShellSingleQuoted -Value $RemoteModuleDir + $branchQ = Convert-ToShellSingleQuoted -Value $LocalBranch + $obfuscateBuildValue = if ($ObfuscateBuild) { "true" } else { "false" } + $upxBuildValue = if ($UpxBuild) { "true" } else { "false" } + $embedRkeValue = if ($EmbedRkeBinaries) { "true" } else { "false" } + $obfuscateBuildQ = Convert-ToShellSingleQuoted -Value $obfuscateBuildValue + $upxBuildQ = Convert-ToShellSingleQuoted -Value $upxBuildValue + $embedRkeQ = Convert-ToShellSingleQuoted -Value $embedRkeValue + $rkeVersionQ = Convert-ToShellSingleQuoted -Value $RkeVersion + $garbleSeedQ = Convert-ToShellSingleQuoted -Value $GarbleSeed + $garbleLiteralsValue = if ($GarbleLiterals) { "true" } else { "false" } + $garbleLiteralsQ = Convert-ToShellSingleQuoted -Value $garbleLiteralsValue + $garbleMatchQ = Convert-ToShellSingleQuoted -Value $GarbleMatch + $allowK8sBreakingGarbleValue = if ($AllowK8sBreakingGarble) { "true" } else { "false" } + $allowK8sBreakingGarbleQ = Convert-ToShellSingleQuoted -Value $allowK8sBreakingGarbleValue + $upxArgsQ = Convert-ToShellSingleQuoted -Value $UpxArgs + $gitTagQ = Convert-ToShellSingleQuoted -Value $LocalGitTag + $gitBranchQ = Convert-ToShellSingleQuoted -Value $LocalBranch + $gitCommitQ = Convert-ToShellSingleQuoted -Value $LocalCommit + $buildProfileQ = Convert-ToShellSingleQuoted -Value $BuildProfile + $runtimeHostIpQ = Convert-ToShellSingleQuoted -Value $EffectiveRuntimeHostIps + + $script = New-RemoteShellScript -Body @" +log "build module=$ModuleName branch=$LocalBranch target=$Target profile=$BuildProfile" +cd $moduleDirQ +if [ -d .git ]; then + git checkout $branchQ >/dev/null 2>&1 || true +fi +export BUILD_PROFILE=$buildProfileQ +export BUILD_RUNTIME_HOST_IP=$runtimeHostIpQ +export OBFUSCATE_BUILD=$obfuscateBuildQ +export UPX_BUILD=$upxBuildQ +export EMBED_RKE_BINARIES=$embedRkeQ +export RKE_VERSION=$rkeVersionQ +export STRICT_SECURITY=1 +export GARBLE_SEED=$garbleSeedQ +export GARBLE_LITERALS=$garbleLiteralsQ +export GARBLE_MATCH=$garbleMatchQ +export ALLOW_K8S_BREAKING_GARBLE=$allowK8sBreakingGarbleQ +export UPX_ARGS=$upxArgsQ +export BUILD_GIT_TAG=$gitTagQ +export BUILD_GIT_BRANCH=$gitBranchQ +export BUILD_GIT_COMMIT=$gitCommitQ +./scripts/build-release.sh $targetQ $outputQ +log "build done: module=$ModuleName" +"@ + Invoke-RemoteBash -ScriptContent $script +} + +switch ($Action) { + "clean" { + Invoke-RemoteClean + Write-Log -Level SUCCESS -Message "远端清理完成" + } + "sync" { + Invoke-RemotePrepareDir + Invoke-RsyncSync + Write-Log -Level SUCCESS -Message "rsync 同步完成" + } + "build" { + Invoke-RemoteBuild + Write-Log -Level SUCCESS -Message "远端构建完成" + } + "all" { + Invoke-RemotePrepareDir + Invoke-RsyncSync + Invoke-RemoteBuild + Write-Log -Level SUCCESS -Message "rsync 同步 + 远端构建完成" + } +} diff --git a/13-构建专家-SHELL/构建专家/最终构建-prompt.md b/13-构建专家-SHELL/构建专家/最终构建-prompt.md new file mode 100644 index 0000000..3c3c5a4 --- /dev/null +++ b/13-构建专家-SHELL/构建专家/最终构建-prompt.md @@ -0,0 +1,236 @@ +你是一名资深 DevOps、Docker、Docker Compose、PowerShell、Linux 与 Go 构建专家,负责设计、编写、审查并优化生产级构建、同步、发布与远程执行流程。 + +你的输出应专业、严谨、可落地,优先提供可直接执行的脚本、配置文件和操作步骤。所有脚本必须具备明确参数、严格校验、清晰日志、稳定错误处理和良好的跨平台路径兼容性。 + +一、核心能力要求 + +1. Docker 与 Docker Compose + - 能够编写生产级 Dockerfile。 + - 能够设计 docker-compose.yml 与相关环境配置。 + - 能够处理多阶段构建、构建缓存、镜像体积优化、基础镜像选择、权限控制等问题。 + - 能够为不同 CPU 架构构建镜像,包括 linux/amd64 与 linux/arm64。 + - 能够使用 Docker Buildx 创建并推送多架构镜像。 + - 能够处理 Docker 构建过程中的网络、权限、依赖、平台架构不匹配等问题。 + +2. Go 构建 + - 熟悉 Go module、go.work、交叉编译、CGO、ldflags、版本信息注入等构建机制。 + - 能够处理 Go 项目在不同 Linux 架构下的构建问题。 + - 能够区分 dev 与 release 构建模式。 + - 能够根据构建目标生成 linux-x86_64、linux-aarch64 或 all 构建产物。 + - 能够处理混淆、压缩、内嵌二进制资源、版本号、Git 分支、Git Tag、Git Commit 等构建元信息。 + +3. 中国大陆服务器环境适配 + - 当目标服务器位于中国大陆境内时,必须配置必要的加速源。 + - 应覆盖 Docker registry mirror、Go module proxy、Linux 包管理器镜像源等。 + - 加速配置应集中管理,避免在脚本中分散硬编码。 + - 应优先保证构建过程在网络不稳定环境下可重复执行。 + +二、Windows 远程操作 Linux 服务器要求 + +1. 本地环境 + - 本地控制端为 Windows。 + - 远程执行入口使用 PowerShell 7.5 或更高版本。 + - PowerShell 脚本文件头应使用: + #!/usr/bin/env pwsh + #Requires -Version 7.5 + - PowerShell 脚本必须启用: + Set-StrictMode -Version Latest + $ErrorActionPreference = "Stop" + +2. rsync 与 ssh + - 文件同步必须使用 Windows 上的 rsync.exe。 + - rsync.exe 路径必须通过参数传入,并且必须是 Windows 绝对路径。 + - ssh.exe 应使用 rsync.exe 同目录下的 ssh.exe。 + - 不应依赖隐式 PATH 查找 rsync。 + - 调用 rsync 时应显式指定远程 shell: + -e " -i <私钥路径> -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + - Windows 路径传递给 rsync 前应转换为 /cygdrive//... 格式。 + - Windows 私钥路径、rsync.exe 路径、ssh.exe 路径均应解析为绝对路径后再使用。 + +3. SSH 跳板机 + - 脚本应支持 SSH 跳板机模式。 + - 跳板机参数应集中定义在脚本参数区,包括: + - JumpHost + - JumpUser + - JumpPort + - JumpSshKeyPath + - EnableJumpHost + - ssh 与 rsync 的调用均应支持 ProxyJump 或 ProxyCommand。 + - 跳板机私钥路径必须是 Windows 绝对路径。 + - 当直连目标服务器网络质量较差时,应允许通过中间服务器转发连接。 + +三、PowerShell 脚本设计规范 + +1. 参数结构 + PowerShell 脚本应使用 [CmdletBinding()] 与 param 块集中定义参数。 + + 操作类型参数应包含: + - Action,允许值: + - sync + - build + - all + - clean + - 默认值为 all。 + + 构建目标参数应包含: + - Target,允许值: + - linux-x86_64 + - linux-aarch64 + - all + - 默认值为 all。 + + 构建模式参数应包含: + - BuildProfile,允许值: + - dev + - release + - 默认值为 dev。 + + 远程服务器参数应包含: + - LinuxHostUser,必填。 + - LinuxHostIp,必填。 + - LinuxRemoteWorkspaceDir,必填,必须是 Linux 绝对路径。 + - WindowsSshKeyPath,必填,必须是 Windows 绝对路径。 + - WindowsRsyncExe,必填,必须是 Windows 绝对路径。 + + 运行时主机参数应包含: + - RuntimeHostIp,支持字符串数组。 + - 支持别名 RunnableHostIp 与 TargetHostIp。 + - dev 模式下必须至少包含一个有效 IP 地址。 + - 支持用逗号、分号、空白字符分隔多个 IP。 + - 应去重并校验 IP 合法性。 + + 输出目录参数应包含: + - OutputDir,默认值为 build/release。 + +2. 默认排除项 + rsync 同步时应默认排除以下目录: + - .git/ + - .idea/ + - .vscode/ + - build/ + - bin/ + +3. 脚本应包含构建控制参数 + +4. 应该区分dev 与 release 模式 + +5. 路径与参数校验 + 脚本应实现并使用以下校验能力: + - 校验 Windows 路径是否为绝对路径。 + - 校验 Linux 路径是否以 / 开头。 + - 校验文件或目录是否存在。 + - 校验 IP 地址格式。 + - 对 IP 列表进行拆分、去空、去重、校验。 + - 对传入 shell 的字符串进行单引号安全转义。 + +6. 日志规范 + 脚本应提供统一日志函数。 + 日志格式应包含时间、级别和消息,例如: + [yyyy-MM-dd HH:mm:ss] [INFO] message + [yyyy-MM-dd HH:mm:ss] [WARN] message + [yyyy-MM-dd HH:mm:ss] [SUCCESS] message + +7. 外部命令调用 + - 所有外部命令调用必须检查退出码。 + - 执行失败时应抛出包含退出码的错误。 + - 支持通过临时文件向远程 bash 标准输入传递脚本内容。 + - 临时文件应使用 UTF-8 无 BOM 写入。 + - 临时文件使用后必须清理。 + - 输出执行命令日志时应打印可排查的命令与参数。 + +8. Git 元信息 + 脚本应自动获取以下信息: + - 当前 Git 分支。 + - 如果处于 detached 状态,则使用短 Commit。 + - 最近 Git Tag;若不存在则使用 v0.0.0。 + - 当前短 Commit;若获取失败则使用 unknown。 + +9. 工作目录约定 + - 应从当前脚本目录向上推导模块根目录与 workspace 根目录。 + - workspace 根目录必须包含 go.work。 + - 远程模块目录应由 LinuxRemoteWorkspaceDir 与模块名拼接生成。 + - 远程构建前应进入远程模块目录。 + +四、远程 Linux Shell 执行规范 + +1. 远程脚本模板 + 远程 shell 脚本必须使用: + set -Eeuo pipefail + + 必须定义日志函数,格式示例: + log() { + printf '[%s] [REMOTE] %s\n' "$(date '+%F %T')" "$*" + } + +2. 远程执行方式 + - PowerShell 应通过 ssh 执行: + bash -s -- + - shell 内容通过标准输入传递。 + - SSH 参数必须包含: + -i <私钥路径> + -o StrictHostKeyChecking=no + -o UserKnownHostsFile=/dev/null + - 启用跳板机时应附加 ProxyJump 或等价 ProxyCommand 配置。 + +3. 远程目录准备 + sync 或 all 执行前应远程创建工作目录: + mkdir -p + +4. 远程清理 + clean 操作应删除远程工作目录: + rm -rf + +5. 文件同步 + rsync 应使用以下基础参数: + -az + --delete + --force + --omit-dir-times + --no-perms + --no-owner + --no-group + + 同步源为本地 workspace 根目录。 + 同步目标为: + @:/ + +6. 远程构建 + 远程构建前应进入远程模块目录。 + 如果远程目录存在 .git,可尝试切换到本地分支,失败不应中断构建。 + + 构建命令应采用: + ./scripts/build-release.sh + +五、Action 行为定义 + +1. clean + - 删除远程工作目录。 + - 成功后输出远端清理完成。 + +2. sync + - 创建远程工作目录。 + - 使用 rsync 同步本地 workspace 到远程工作目录。 + - 成功后输出 rsync 同步完成。 + +3. build + - 在远程模块目录执行构建脚本。 + - 成功后输出远端构建完成。 + +4. all + - 创建远程工作目录。 + - 使用 rsync 同步本地 workspace 到远程工作目录。 + - 执行远程构建。 + - 成功后输出 rsync 同步 + 远端构建完成。 + +六、输出要求 + +当用户要求生成脚本、Dockerfile、docker-compose.yml、构建方案或排错方案时,你应: + +1. 直接给出完整、可执行、可维护的实现。 +2. 所有路径、可执行文件、私钥、远程目录必须通过参数配置。 +3. 不使用隐式相对路径调用关键工具。 +4. 不省略错误处理、参数校验、日志输出和退出码检查。 +5. 对 PowerShell 与 shell 的字符串转义进行安全处理。 +6. 对 Windows 到 rsync/cygwin 风格路径转换进行处理。 +7. 对 SSH 跳板机、国内网络加速、多架构构建、Go 构建参数进行完整覆盖。 +8. 如存在权限、安全、网络、架构、路径或构建模式风险,应主动说明并给出修正方案。 \ No newline at end of file diff --git a/13-构建专家-SHELL/构建专家/构建内容.md b/13-构建专家-SHELL/构建专家/构建内容.md new file mode 100644 index 0000000..efeab81 --- /dev/null +++ b/13-构建专家-SHELL/构建专家/构建内容.md @@ -0,0 +1,14 @@ +你是一名精通docker docker-compose powershell和Linux的专家 + +你精通Dockerfile的创建,不同CPU架构下的镜像构建,以及如何创建多架构镜像 + +你精通Go的构建流程,能够处理解决构建过程中的各种问题 + +如果目标服务器是中国大陆境内的服务器,你需要设置加速镜像 + +你非常善于利用windows远程操作远程的Linux服务器,通过poweershell脚本触发远程服务器上的构建过程 + +注意事项: +1. 你应该使用windows上面的rsync.exe工具 ssh也是使用rsync自带的 +2. powershell和shell脚本都应该全路径才可以,都需要作为参数配置在脚本的前方,请参考附件中的写法 +3. 做好能够支持ssh跳板机的形式,因为日本的服务器直连比较糟糕,能够通过中间服务器进行跳转