From e0371aeea0b7f738be1a84c24e2a9b5a0066e406 Mon Sep 17 00:00:00 2001 From: Shiroha7z Date: Sat, 3 Oct 2026 10:41:20 +0800 Subject: [PATCH 1/2] feat(desktop): attest formal release artifacts and publish SHA256 --- .changeset/desktop-formal-provenance.md | 6 + .github/workflows/desktop-release.yml | 115 ++++++++++++------ README.md | 6 +- docs/releases/desktop.md | 35 ++++++ docs/zh/guide/desktop.md | 30 +++-- packages/desktop/.gitignore | 1 + packages/desktop/scripts/package.mjs | 4 +- packages/desktop/src/main/main.ts | 3 +- packages/desktop/src/main/runtime.ts | 2 +- .../src/renderer/dialogs/AboutDialog.tsx | 61 ++++++++-- packages/tools/lib/npm-publish-packages.mjs | 1 + 11 files changed, 194 insertions(+), 70 deletions(-) create mode 100644 .changeset/desktop-formal-provenance.md create mode 100644 docs/releases/desktop.md diff --git a/.changeset/desktop-formal-provenance.md b/.changeset/desktop-formal-provenance.md new file mode 100644 index 00000000..52bc5a91 --- /dev/null +++ b/.changeset/desktop-formal-provenance.md @@ -0,0 +1,6 @@ +--- +"@sfmc-bds/cli": patch +"@sfmc-bds/management": patch +--- + +桌面正式发行提供 GitHub 构建证明和公开 SHA256,客户端通过发行页手动更新。将管理协议包纳入发行材料与版本验证清单。 diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 9e8c371e..34225b6c 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -15,9 +15,9 @@ on: workflow_dispatch: inputs: signed: - description: "生成签名发行包(需要配置 PFX Secrets 与发布者变量)" + description: "使用已配置证书生成 Windows 签名包" type: boolean - default: true + default: false permissions: contents: read @@ -28,19 +28,18 @@ jobs: env: ELECTRON_CACHE: ${{ github.workspace }}\.cache\electron ELECTRON_BUILDER_CACHE: ${{ github.workspace }}\.cache\electron-builder + SFMC_RELEASE_SIGNED: ${{ inputs.signed || (startsWith(github.ref, 'refs/tags/desktop-v') && vars.SFMC_DESKTOP_SIGNING_ENABLED == 'true') }} steps: - uses: actions/checkout@v4 - - name: 核对正式签名配置 - if: startsWith(github.ref, 'refs/tags/desktop-v') || inputs.signed + - name: 核对签名配置 + if: inputs.signed || (startsWith(github.ref, 'refs/tags/desktop-v') && vars.SFMC_DESKTOP_SIGNING_ENABLED == 'true') shell: pwsh env: CSC_LINK: ${{ secrets.CSC_LINK }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} SFMC_SIGNING_PUBLISHER: ${{ vars.SFMC_SIGNING_PUBLISHER }} run: | - if (!$env:CSC_LINK -or !$env:CSC_KEY_PASSWORD -or !$env:SFMC_SIGNING_PUBLISHER) { - throw "正式签名配置不完整:需要 CSC_LINK、CSC_KEY_PASSWORD 和 SFMC_SIGNING_PUBLISHER" - } + if (!$env:CSC_LINK -or !$env:CSC_KEY_PASSWORD -or !$env:SFMC_SIGNING_PUBLISHER) { throw 'Windows 签名配置不完整' } - uses: pnpm/action-setup@v4 with: version: 10.8.0 @@ -55,78 +54,114 @@ jobs: - run: pnpm --filter @sfmc-bds/management typecheck - run: pnpm --filter @sfmc-bds/cli typecheck - run: pnpm --filter @sfmc-bds/desktop typecheck - - name: 核对桌面标签版本 + - name: 核对桌面标签与平台版本 if: startsWith(github.ref, 'refs/tags/') shell: pwsh run: | $version = (Get-Content packages/desktop/package.json | ConvertFrom-Json).version - $prefix = if ($env:GITHUB_REF_NAME.StartsWith('desktop-preview-v')) { 'desktop-preview-v' } else { 'desktop-v' } - if ($prefix -eq 'desktop-preview-v' -and $version -notmatch '-preview\.\d+$') { throw "预览标签必须使用 preview 版本号" } - if ($env:GITHUB_REF_NAME -ne "$prefix$version") { throw "桌面标签与 package.json 版本不一致" } + $platform = (Get-Content packages/meta/package.json | ConvertFrom-Json).version + $preview = $env:GITHUB_REF_NAME.StartsWith('desktop-preview-v') + $prefix = if ($preview) { 'desktop-preview-v' } else { 'desktop-v' } + if ($env:GITHUB_REF_NAME -ne "$prefix$version") { throw '桌面标签与源码版本不一致' } + if ($preview -and $version -notmatch '-preview\.\d+$') { throw '预览标签必须使用 preview 版本号' } + if (!$preview) { + if ($version -notmatch '^\d+\.\d+\.\d+$' -or $platform -notmatch '^\d+\.\d+\.\d+$') { throw '正式桌面与内置平台必须使用稳定版本号' } + $published = pnpm view "@sfmc-bds/sfmc@$platform" version --json | ConvertFrom-Json + if ($LASTEXITCODE -ne 0 -or $published -ne $platform) { throw '匹配的平台版本尚未发布到 npm' } + } - name: 打包 shell: pwsh env: - CSC_LINK: ${{ (startsWith(github.ref, 'refs/tags/desktop-v') || inputs.signed) && secrets.CSC_LINK || '' }} - CSC_KEY_PASSWORD: ${{ (startsWith(github.ref, 'refs/tags/desktop-v') || inputs.signed) && secrets.CSC_KEY_PASSWORD || '' }} - SFMC_SIGNING_PUBLISHER: ${{ (startsWith(github.ref, 'refs/tags/desktop-v') || inputs.signed) && vars.SFMC_SIGNING_PUBLISHER || '' }} + CSC_LINK: ${{ (inputs.signed || (startsWith(github.ref, 'refs/tags/desktop-v') && vars.SFMC_DESKTOP_SIGNING_ENABLED == 'true')) && secrets.CSC_LINK || '' }} + CSC_KEY_PASSWORD: ${{ (inputs.signed || (startsWith(github.ref, 'refs/tags/desktop-v') && vars.SFMC_DESKTOP_SIGNING_ENABLED == 'true')) && secrets.CSC_KEY_PASSWORD || '' }} + SFMC_SIGNING_PUBLISHER: ${{ (inputs.signed || (startsWith(github.ref, 'refs/tags/desktop-v') && vars.SFMC_DESKTOP_SIGNING_ENABLED == 'true')) && vars.SFMC_SIGNING_PUBLISHER || '' }} run: | - if ($env:GITHUB_REF -like 'refs/tags/desktop-v*' -or '${{ inputs.signed }}' -eq 'true') { - pnpm desktop:package --signed - } else { - pnpm desktop:package - } - if ($LASTEXITCODE -ne 0) { throw "桌面打包失败" } - - name: 验证签名身份 - if: startsWith(github.ref, 'refs/tags/desktop-v') || inputs.signed + if ($env:SFMC_RELEASE_SIGNED -eq 'true') { pnpm desktop:package --signed } else { pnpm desktop:package } + if ($LASTEXITCODE -ne 0) { throw '桌面打包失败' } + - name: 验证实际签名状态并整理发行材料 shell: pwsh env: SFMC_SIGNING_PUBLISHER: ${{ vars.SFMC_SIGNING_PUBLISHER }} run: | + $manifest = Get-Content packages/desktop/payload/release-manifest.json | ConvertFrom-Json + $signed = $env:SFMC_RELEASE_SIGNED -eq 'true' + if ($manifest.windowsCodeSigned -ne $signed -or $manifest.sourceCommit -ne $env:GITHUB_SHA) { throw '发行清单与实际构建不一致' } $files = @(Get-ChildItem packages/desktop/release -Filter '*.exe') $files += Get-Item 'packages/desktop/release/win-unpacked/SFMC Desktop.exe' foreach ($file in $files) { $signature = Get-AuthenticodeSignature -LiteralPath $file.FullName - if ($signature.Status -ne 'Valid') { throw "签名无效:$($file.Name)" } - if ($signature.SignerCertificate.GetNameInfo([System.Security.Cryptography.X509Certificates.X509NameType]::SimpleName, $false) -ne $env:SFMC_SIGNING_PUBLISHER) { throw "签名发布者不匹配" } + if ($signed) { + if ($signature.Status -ne 'Valid' -or $signature.SignerCertificate.GetNameInfo([System.Security.Cryptography.X509Certificates.X509NameType]::SimpleName, $false) -ne $env:SFMC_SIGNING_PUBLISHER) { throw "签名身份验证失败:$($file.Name)" } + } elseif ($signature.Status -ne 'NotSigned') { throw "未签名发行出现意外签名:$($file.Name)" } } - - name: 生成下载校验文件 - shell: pwsh - run: | - Get-ChildItem packages/desktop/release -File | Where-Object { $_.Extension -in '.exe', '.zip' } | Sort-Object Name | ForEach-Object { "$(Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 | Select-Object -ExpandProperty Hash) $($_.Name)" } | Set-Content packages/desktop/release/SHA256SUMS.txt -Encoding utf8 + New-Item -ItemType Directory -Path packages/desktop/distribution -Force | Out-Null + Get-ChildItem packages/desktop/release -File | Where-Object { $_.Extension -in '.exe', '.zip' -or ($signed -and $_.Extension -in '.yml', '.blockmap') } | Copy-Item -Destination packages/desktop/distribution + Copy-Item packages/desktop/payload/release-manifest.json packages/desktop/distribution + $lines = Get-ChildItem packages/desktop/distribution -File | Sort-Object Name | ForEach-Object { "$( (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()) $($_.Name)" } + [IO.File]::WriteAllText((Join-Path $env:GITHUB_WORKSPACE 'packages/desktop/distribution/SHA256SUMS.txt'), ($lines -join "`n") + "`n", [Text.UTF8Encoding]::new($false)) - uses: actions/upload-artifact@v4 with: name: sfmc-desktop-windows-x64 - path: | - packages/desktop/release/*.exe - packages/desktop/release/*.zip - packages/desktop/release/*.yml - packages/desktop/release/*.blockmap - packages/desktop/release/SHA256SUMS.txt - packages/desktop/payload/release-manifest.json + path: packages/desktop/distribution/* if-no-files-found: error + publish: if: startsWith(github.ref, 'refs/tags/') needs: windows runs-on: ubuntu-24.04 permissions: contents: write + id-token: write + attestations: write + artifact-metadata: write steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: name: sfmc-desktop-windows-x64 path: artifacts + - name: 核对 Windows 构建材料 + working-directory: artifacts + run: sha256sum --check SHA256SUMS.txt + - name: 生成 GitHub 构建证明 + id: provenance + uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 + with: + subject-path: | + artifacts/*.exe + artifacts/*.zip + artifacts/release-manifest.json + artifacts/*.yml + artifacts/*.blockmap + - name: 公示 SHA256 和构建证明 + env: + ATTESTATION_BUNDLE: ${{ steps.provenance.outputs.bundle-path }} + ATTESTATION_URL: ${{ steps.provenance.outputs.attestation-url }} + RELEASE_TAG: ${{ github.ref_name }} + run: | + cp "$ATTESTATION_BUNDLE" artifacts/artifact-attestation.sigstore.json + python3 - <<'PY' + import hashlib, json, os + from pathlib import Path + directory = Path('artifacts') + manifest = json.loads((directory / 'release-manifest.json').read_text()) + rows = [(file.name, hashlib.sha256(file.read_bytes()).hexdigest()) for file in sorted(directory.iterdir()) if file.is_file() and file.name != 'SHA256SUMS.txt'] + (directory / 'SHA256SUMS.txt').write_text(''.join(f'{digest} {name}\n' for name, digest in rows)) + preview = os.environ['RELEASE_TAG'].startswith('desktop-preview-v') + notes = Path('docs/releases/desktop-preview.md' if preview else 'docs/releases/desktop.md').read_text() + notes += f'\n\n源码提交:`{manifest["sourceCommit"]}`。\n\n[GitHub 构建证明]({os.environ["ATTESTATION_URL"]})\n\n### SHA256 公示\n\n| 文件 | SHA256 |\n| --- | --- |\n' + notes += ''.join(f'| `{name}` | `{digest}` |\n' for name, digest in rows) + Path('release-notes.md').write_text(notes) + PY - name: 发布独立桌面版本 env: GH_TOKEN: ${{ github.token }} GH_REPO: ${{ github.repository }} RELEASE_TAG: ${{ github.ref_name }} run: | + mapfile -t assets < <(find artifacts -type f) if [[ "$RELEASE_TAG" == desktop-preview-v* ]]; then - mapfile -t assets < <(find artifacts -type f \( -name '*.exe' -o -name '*.zip' -o -name 'release-manifest.json' -o -name 'SHA256SUMS.txt' \)) - gh release create "$RELEASE_TAG" "${assets[@]}" --title "SFMC Desktop ${RELEASE_TAG#desktop-preview-v}(未签名预览)" --notes-file docs/releases/desktop-preview.md --prerelease --latest=false + gh release create "$RELEASE_TAG" "${assets[@]}" --title "SFMC Desktop ${RELEASE_TAG#desktop-preview-v}" --notes-file release-notes.md --prerelease --latest=false else - mapfile -t assets < <(find artifacts -type f) - gh release create "$RELEASE_TAG" "${assets[@]}" --title "SFMC Desktop ${RELEASE_TAG#desktop-v}" --generate-notes --latest=false + gh release create "$RELEASE_TAG" "${assets[@]}" --title "SFMC Desktop ${RELEASE_TAG#desktop-v}" --notes-file release-notes.md --latest=false fi - diff --git a/README.md b/README.md index bb38b671..6e1023fd 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,11 @@ SFMC 希望通过模块化架构补充基岩版的原生开发体验。 ### 桌面版(Windows x64) -[下载未签名预发布版](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-preview-v0.1.0-preview.2) · [使用文档](./docs/zh/guide/desktop.md) +[下载桌面正式版](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.0) · [使用文档](./docs/zh/guide/desktop.md) -SFMC Desktop 支持本机与 SSH 实例,提供服务启停、日志控制台、模块与资源包、配置编辑、玩家管理和运行指标。下载 `.exe` 安装,或完整解压 `.zip` 后启动;内置 Node.js、pnpm 与预览平台。 +SFMC Desktop 支持本机与 SSH 实例,提供服务启停、日志控制台、模块与资源包、配置编辑、玩家管理和运行指标。下载 `.exe` 安装,或完整解压 `.zip` 后启动;内置 Node.js、pnpm 与平台代码。 -添加实例时选择 **SFMC 部署根目录**(例如 `D:\WorkPlace\SFMC`)。已有正式部署建议先选择“仅查看”,完整预览使用独立测试目录。当前安装包尚未签名,请核对发行页与校验值;预览版通过发行页手动更新。 +添加实例时选择 **SFMC 部署根目录**(例如 `D:\WorkPlace\SFMC`)。已有部署建议先选择“仅查看”,首次验收使用独立测试目录。发行页公示 SHA256,并提供 GitHub Artifact Attestations 构建证明;客户端通过发行页手动更新。 ![SFMC Desktop 概览](./docs/public/desktop/overview.jpg) diff --git a/docs/releases/desktop.md b/docs/releases/desktop.md new file mode 100644 index 00000000..d0734382 --- /dev/null +++ b/docs/releases/desktop.md @@ -0,0 +1,35 @@ +SFMC Desktop **0.1.0**,首个 Windows x64 正式版。 + +支持本机与 SSH 实例、服务启停、控制台、模块与资源包、配置、玩家、维护任务和运行指标。内置平台 **0.2.9**、Node.js 与 pnpm。本机 Windows 与 SSH Windows 的数据服务/BDS、真实指标和守护进程重连已验收。 + +### 下载与使用 + +1. 下载 `.exe` 安装,或完整解压 `.zip` 后运行 `SFMC Desktop.exe`。 +2. 添加本机实例,选择 SFMC 部署根目录;SSH 实例填写主机、端口、用户、身份验证及远程根目录。 +3. 已有部署先选择“仅查看”并核对接入计划;首次验收可在独立目录按引导初始化。 +4. 在概览启动数据服务和 BDS,通过控制台与任务查看结果。关闭客户端不停止服务器。 + +[完整使用文档与截图](https://sfmc.dogelake.cn/guide/desktop) + +### 下载验证 + +发行页下方公示各文件 SHA256,`SHA256SUMS.txt` 提供可下载的校验清单。下载后在 PowerShell 核对: + +```powershell +Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-x64.exe' -Algorithm SHA256 +``` + +使用 GitHub CLI 验证构建证明及来源标签: + +```powershell +gh attestation verify '.\SFMC-Desktop-0.1.0-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.0 --deny-self-hosted-runners +``` + +ZIP 使用相同命令替换文件名即可。`artifact-attestation.sigstore.json` 是可下载的证明材料,`release-manifest.json` 记录源码提交、平台版本和内置运行时信息。 + +### 更新与兼容 + +- 本版本通过发行页手动更新:退出客户端后覆盖安装或替换完整 ZIP 内容。服务器部署与数据独立于客户端目录。 +- 接入现有部署不会自动切换活动平台。升级前按接入计划核对旧模块及平台兼容性。 +- 平台 0.2.9 停用模块声明中的 `canDisable` 与 `enabledByDefault`;旧模块需移除这些字段。已有启停状态继续由 module-lock 保存。 +- Linux 远程环境、游戏内表单与独立业务模块的完整流程仍需按实际环境验收。 diff --git a/docs/zh/guide/desktop.md b/docs/zh/guide/desktop.md index 72d9289f..f7e05c2b 100644 --- a/docs/zh/guide/desktop.md +++ b/docs/zh/guide/desktop.md @@ -2,31 +2,40 @@ SFMC Desktop 是 Windows x64 桌面客户端,可以管理本机 SFMC 部署,也可以通过 SSH 连接 Linux 或 Windows 服务器。服务启停、控制台、模块、资源包、配置、玩家与维护任务都集中在同一个工作区。 -当前提供 **0.1.0-preview.2 未签名预发布版**。内置平台为 `0.2.9-preview.2`,独立于 npm 正式版;预览版通过发行页手动下载安装更新。 +当前提供 **0.1.0 正式版**,内置平台 `0.2.9`。客户端通过发行页手动下载安装更新。 ## 下载与启动 -从 [桌面版预发布页](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-preview-v0.1.0-preview.2) 下载: +从 [桌面版发行页](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.0) 下载: -- `SFMC-Desktop-0.1.0-preview.2-x64.exe`:安装向导,可以选择安装目录。 -- `SFMC-Desktop-0.1.0-preview.2-x64.zip`:完整解压到一个目录,再运行 `SFMC Desktop.exe`。保留全部文件。 +- `SFMC-Desktop-0.1.0-x64.exe`:安装向导,可以选择安装目录。 +- `SFMC-Desktop-0.1.0-x64.zip`:完整解压到一个目录,再运行 `SFMC Desktop.exe`。保留全部文件。 - `SHA256SUMS.txt`:下载校验值;`release-manifest.json`:源代码提交与内置运行时版本。 +- `artifact-attestation.sigstore.json`:GitHub Artifact Attestations 构建证明。 客户端已经包含 Node.js、pnpm 和平台代码,本机无需额外安装这些工具。首次初始化、BDS 下载及联网更新仍需要网络。 -签名申请正在审核,因此 Windows 可能显示“未知发布者”或 SmartScreen 提示。先确认下载来自上面的官方仓库,并核对校验值;是否继续运行由你决定,不需要关闭系统安全功能。 +发行页同时公示 SHA256。下载后核对文件摘要: ```powershell -Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-preview.2-x64.exe' -Algorithm SHA256 +Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-x64.exe' -Algorithm SHA256 ``` +安装 [GitHub CLI](https://cli.github.com/) 后,可核对构建证明、源码仓库、工作流和标签: + +```powershell +gh attestation verify '.\SFMC-Desktop-0.1.0-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.0 --deny-self-hosted-runners +``` + +验证 ZIP 时替换文件名即可;添加 `--bundle '.\artifact-attestation.sigstore.json'` 可以验证下载的证明文件。需要固定到具体提交时,再加入 `--source-digest` 和 `release-manifest.json` 中的 `sourceCommit`。构建证明用于核对文件来源与摘要,不是 Windows 发布者证书。 + ## 添加本机实例 1. 在欢迎页选择 **本机**,填写便于辨认的实例名称。 2. 选择 **SFMC 部署根目录**,例如 `D:\WorkPlace\SFMC`。不要选择源码仓库、`BDS` 子目录或世界目录。 3. 保存后点击 **接入实例**。查看接入计划,再选择 **仅查看** 或完成接入。 -已有部署保留自己的活动平台版本,客户端不会仅因为连接就替换它。旧平台可能只能查看,或者缺少新指标与管理接口。完整预览建议使用独立测试目录(例如 `D:\WorkPlace\SFMC-Desktop-Preview`),按引导确认 Minecraft EULA 并初始化;向导会下载 BDS 并尝试启动服务;不要直接把正式世界搬进去。 +已有部署保留自己的活动平台版本,客户端不会仅因为连接就替换它。旧平台可能只能查看,或者缺少新指标与管理接口。首次验收建议使用独立测试目录(例如 `D:\WorkPlace\SFMC-Desktop-Test`),按引导确认 Minecraft EULA 并初始化;向导会下载 BDS 并尝试启动服务。 ## 添加 SSH 实例 @@ -54,7 +63,7 @@ Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-preview.2-x64.exe' -Algorithm SH ![模块管理:模块列表和详情](../../public/desktop/modules.jpg) -**模块** 查看来源、版本和启用状态。修改后根据任务提示重启 BDS,才会应用需要重新打包的变更。预览版不代表所有独立业务模块都已适配。 +**模块** 查看来源、版本和启用状态。修改后根据任务提示重启 BDS,才会应用需要重新打包的变更。平台 0.2.9 停用模块声明中的 `canDisable` 与 `enabledByDefault`,旧模块需移除这些字段;已有启停状态仍由 module-lock 保存。 ![配置编辑:配置列表与编辑器](../../public/desktop/config.jpg) @@ -67,11 +76,10 @@ Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-preview.2-x64.exe' -Algorithm SH 3. 查看模块与配置,尝试一项可撤销的修改,并确认任务完成。 4. 关闭客户端后重新打开,确认能连接原守护进程。关闭窗口不会停止服务器;需要停止时在概览明确停止 BDS 和数据服务。 -预览版本暂不接入稳定版自动更新。后续版本从发行页下载,安装到原客户端目录即可;部署根目录和服务器数据独立于客户端安装目录。 +本版本通过发行页手动更新。后续版本从发行页下载,退出客户端后安装到原客户端目录,或完整替换 ZIP 内容;部署根目录和服务器数据独立于客户端安装目录。 ## 当前边界 -- 安装包尚未签名,正式签名版将在证书批准和工作流接入后另行发布。 - 本机 Windows 与 SSH Windows 的数据服务/BDS、实际指标和断线重连已经验收;其他环境仍需自行验证。 -- 旧活动平台不会自动切换到预览平台。npm 的稳定版号也不能证明它已具备这次预览的全部接口。 +- 接入时不会自动切换旧活动平台。需要新版接口时,按接入计划核对兼容性并升级。 - 游戏内表单点击与独立模块的全部业务流程仍需要 Minecraft 客户端验收。 diff --git a/packages/desktop/.gitignore b/packages/desktop/.gitignore index 641a363c..a1d21cfe 100644 --- a/packages/desktop/.gitignore +++ b/packages/desktop/.gitignore @@ -1,5 +1,6 @@ staging/ payload/ release/ +distribution/ .pack-work/ .pack-cache/ diff --git a/packages/desktop/scripts/package.mjs b/packages/desktop/scripts/package.mjs index cced4541..46705b1e 100644 --- a/packages/desktop/scripts/package.mjs +++ b/packages/desktop/scripts/package.mjs @@ -8,6 +8,7 @@ const base = path.resolve(fileURLToPath(new URL('..', import.meta.url))); const root = path.resolve(base, '../..'); const manifest = JSON.parse(await fs.readFile(path.join(base, 'runtime-manifest.json'), 'utf8')); const desktop = JSON.parse(await fs.readFile(path.join(base, 'package.json'), 'utf8')); +const signed = process.argv.includes('--signed'); const material = path.join(base, 'payload'); const work = path.join(base, '.pack-work'); const staging = path.join(base, 'staging'); @@ -119,10 +120,9 @@ const winswHash = await download('https://github.com/winsw/winsw/releases/downlo const materials = []; for (const name of ['platform.tar', 'pnpm.tar', 'WinSW-x64.exe']) materials.push({ name, sha256: createHash('sha256').update(await fs.readFile(path.join(material, name))).digest('hex') }); const sourceCommit = process.env.GITHUB_SHA ?? execFileSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8', windowsHide: true }).trim(); -await fs.writeFile(path.join(material, 'release-manifest.json'), JSON.stringify({ sourceCommit, desktopVersion: desktop.version, platformVersion: platformPackage.version, node: { version: manifest.node, sha256: nodeHash }, pnpm: manifest.pnpm, winsw: { version: '2.12.0', sha256: winswHash }, protocolVersion: manifest.protocolVersion, packages: [...ledger.values()].sort((a, b) => a.name.localeCompare(b.name)), materials }, null, 2)); +await fs.writeFile(path.join(material, 'release-manifest.json'), JSON.stringify({ sourceCommit, desktopVersion: desktop.version, platformVersion: platformPackage.version, windowsCodeSigned: signed, node: { version: manifest.node, sha256: nodeHash }, pnpm: manifest.pnpm, winsw: { version: '2.12.0', sha256: winswHash }, protocolVersion: manifest.protocolVersion, packages: [...ledger.values()].sort((a, b) => a.name.localeCompare(b.name)), materials }, null, 2)); await fs.cp(path.join(material, 'release-manifest.json'), path.join(staging, 'release-manifest.json')); if (!process.argv.includes('--prepare-only')) { - const signed = process.argv.includes('--signed'); if (signed && (!process.env.CSC_LINK || !process.env.CSC_KEY_PASSWORD || !process.env.SFMC_SIGNING_PUBLISHER)) throw new Error('签名发行需要 CSC_LINK、CSC_KEY_PASSWORD 和 SFMC_SIGNING_PUBLISHER;不会生成伪签名发行包'); await command(process.execPath, [path.join(base, 'node_modules/electron-builder/out/cli/cli.js'), '--config', path.join(base, 'electron-builder.yml'), '--win', '--x64', '--publish', 'never', ...(signed ? ['--config.forceCodeSigning=true', `--config.win.publisherName=${process.env.SFMC_SIGNING_PUBLISHER}`] : ['--config.win.signExecutable=false'])], base); } diff --git a/packages/desktop/src/main/main.ts b/packages/desktop/src/main/main.ts index 66278bf1..3e13367b 100644 --- a/packages/desktop/src/main/main.ts +++ b/packages/desktop/src/main/main.ts @@ -3,6 +3,7 @@ import path from "node:path"; import fs from "node:fs"; import { trustHost } from "./hosts.js"; import { autoUpdater } from "electron-updater"; +import { releaseManifest } from "./runtime.js"; import { Session } from "./sessions.js"; import { profiles, profile, saveProfile, removeProfile, credentials, rememberSession } from "./profiles.js"; import type { AppearanceMode, Credentials, InstanceProfile } from "../shared/api.js"; @@ -95,7 +96,7 @@ void app.whenReady().then(async () => { return shell.openExternal(urls[kind as keyof typeof urls]); }); handle("update", async action => { - if (app.getVersion().includes("-")) return { portable: true, downloadUrl: "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases" }; + if (app.getVersion().includes("-") || (app.isPackaged && releaseManifest().windowsCodeSigned !== true)) return { manual: true, downloadUrl: "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases" }; if (app.isPackaged && !fs.existsSync(path.join(path.dirname(process.execPath), "installed.json"))) return { portable: true, downloadUrl: "https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases" }; if (!app.isPackaged) return { development: true }; if (action === "check") { diff --git a/packages/desktop/src/main/runtime.ts b/packages/desktop/src/main/runtime.ts index 51d2c8e3..68df5c53 100644 --- a/packages/desktop/src/main/runtime.ts +++ b/packages/desktop/src/main/runtime.ts @@ -5,7 +5,7 @@ import { spawn } from "node:child_process"; import { app } from "electron"; export function payload() { return app.isPackaged ? path.join(process.resourcesPath, "payload") : path.join(app.getAppPath(), "payload"); } -export function releaseManifest(): { platformVersion: string; node: { version: string }; pnpm: string; materials: { name: string; sha256: string }[] } { +export function releaseManifest(): { platformVersion: string; windowsCodeSigned?: boolean; node: { version: string }; pnpm: string; materials: { name: string; sha256: string }[] } { return JSON.parse(fs.readFileSync(path.join(payload(), "release-manifest.json"), "utf8")); } export function verifyMaterial(name: string) { diff --git a/packages/desktop/src/renderer/dialogs/AboutDialog.tsx b/packages/desktop/src/renderer/dialogs/AboutDialog.tsx index f1700bd9..7193344d 100644 --- a/packages/desktop/src/renderer/dialogs/AboutDialog.tsx +++ b/packages/desktop/src/renderer/dialogs/AboutDialog.tsx @@ -2,10 +2,10 @@ * AboutDialog.tsx — 关于与客户端更新 * * 使用场景:侧栏页脚"关于与更新"、命令面板"关于与检查更新"。 - * 打开时自动执行一次 update("check"),三种形态的处理与原实现一致: - * - 便携版:提示下载最新 ZIP 替换,提供"打开发行页面"; + * 打开时自动执行一次 update("check"): + * - 手动更新或便携版:提供发行页下载入口; * - 开发模式:不执行客户端安装升级; - * - 安装版:有新版本时可"下载更新",下载完成后才可"安装并重启客户端"。 + * - 支持自动更新的安装版:下载完成后可"安装并重启客户端"。 * 同时列出界面所用的设计资源与开源字体/图标/组件的许可声明。 */ import { useEffect, useState } from "react"; @@ -18,7 +18,13 @@ import { Badge, Callout } from "../components/ui.js"; import { errorText } from "../lib/format.js"; /** update("check") 的返回形状(与原实现的收窄一致) */ -interface UpdateInfo { portable?: boolean; development?: boolean; available?: boolean; releaseNotes?: string } +interface UpdateInfo { + manual?: boolean; + portable?: boolean; + development?: boolean; + available?: boolean; + releaseNotes?: string; +} /** 第三方资源声明 */ const CREDITS: { name: string; by: string; license: string }[] = [ @@ -92,17 +98,25 @@ export function AboutDialog({ open, onClose }: { open: boolean; onClose: () => v

客户端更新

- +
{checking && !info ? ( ) : checkError ? ( - {checkError} - ) : info?.portable ? ( + + {checkError} + + ) : info?.manual || info?.portable ? ( <> -

便携版请下载最新 ZIP,退出客户端后替换应用文件。实例和后台继续运行。

+

+ 请从发行页下载安装包,或下载 ZIP 后退出客户端并替换应用文件。实例和后台继续运行。 +

- +
) : info?.development ? ( @@ -110,12 +124,35 @@ export function AboutDialog({ open, onClose }: { open: boolean; onClose: () => v ) : info ? ( <>

- {info.available ? 有新的桌面版本 : 已是最新桌面版本} + {info.available ? ( + + 有新的桌面版本 + + ) : ( + + 已是最新桌面版本 + + )}

{info.releaseNotes &&
{info.releaseNotes}
}
- - + +
) : null} diff --git a/packages/tools/lib/npm-publish-packages.mjs b/packages/tools/lib/npm-publish-packages.mjs index 65f1b652..3a4dc9a1 100644 --- a/packages/tools/lib/npm-publish-packages.mjs +++ b/packages/tools/lib/npm-publish-packages.mjs @@ -12,6 +12,7 @@ export const NPM_PUBLISH_PACKAGES = { "@sfmc-bds/sdk": "modules/sdk/@sfmc-sdk/package.json", "@sfmc-bds/eslint-plugin": "modules/sdk/@sfmc-eslint-plugin/package.json", "@sfmc-bds/cli": "packages/cli/package.json", + "@sfmc-bds/management": "packages/management/package.json", "@sfmc-bds/db-server": "packages/db-server/package.json", "@sfmc-bds/qq-bridge": "packages/qq-bridge/package.json", "@sfmc-bds/bds-tools": "packages/bds-tools/package.json", From 7c77e9e75fc31a83d2fa5f159cd30c9c6fda0d29 Mon Sep 17 00:00:00 2001 From: Shiroha7z Date: Sat, 3 Oct 2026 10:43:58 +0800 Subject: [PATCH 2/2] docs(desktop): align formal version with generated release plan --- README.md | 2 +- docs/releases/desktop.md | 6 +++--- docs/zh/guide/desktop.md | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6e1023fd..445305e4 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ SFMC 希望通过模块化架构补充基岩版的原生开发体验。 ### 桌面版(Windows x64) -[下载桌面正式版](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.0) · [使用文档](./docs/zh/guide/desktop.md) +[下载桌面正式版](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.1) · [使用文档](./docs/zh/guide/desktop.md) SFMC Desktop 支持本机与 SSH 实例,提供服务启停、日志控制台、模块与资源包、配置编辑、玩家管理和运行指标。下载 `.exe` 安装,或完整解压 `.zip` 后启动;内置 Node.js、pnpm 与平台代码。 diff --git a/docs/releases/desktop.md b/docs/releases/desktop.md index d0734382..cd830f45 100644 --- a/docs/releases/desktop.md +++ b/docs/releases/desktop.md @@ -1,4 +1,4 @@ -SFMC Desktop **0.1.0**,首个 Windows x64 正式版。 +SFMC Desktop **0.1.1**,首个 Windows x64 正式版。 支持本机与 SSH 实例、服务启停、控制台、模块与资源包、配置、玩家、维护任务和运行指标。内置平台 **0.2.9**、Node.js 与 pnpm。本机 Windows 与 SSH Windows 的数据服务/BDS、真实指标和守护进程重连已验收。 @@ -16,13 +16,13 @@ SFMC Desktop **0.1.0**,首个 Windows x64 正式版。 发行页下方公示各文件 SHA256,`SHA256SUMS.txt` 提供可下载的校验清单。下载后在 PowerShell 核对: ```powershell -Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-x64.exe' -Algorithm SHA256 +Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.1-x64.exe' -Algorithm SHA256 ``` 使用 GitHub CLI 验证构建证明及来源标签: ```powershell -gh attestation verify '.\SFMC-Desktop-0.1.0-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.0 --deny-self-hosted-runners +gh attestation verify '.\SFMC-Desktop-0.1.1-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.1 --deny-self-hosted-runners ``` ZIP 使用相同命令替换文件名即可。`artifact-attestation.sigstore.json` 是可下载的证明材料,`release-manifest.json` 记录源码提交、平台版本和内置运行时信息。 diff --git a/docs/zh/guide/desktop.md b/docs/zh/guide/desktop.md index f7e05c2b..3af09f6d 100644 --- a/docs/zh/guide/desktop.md +++ b/docs/zh/guide/desktop.md @@ -2,14 +2,14 @@ SFMC Desktop 是 Windows x64 桌面客户端,可以管理本机 SFMC 部署,也可以通过 SSH 连接 Linux 或 Windows 服务器。服务启停、控制台、模块、资源包、配置、玩家与维护任务都集中在同一个工作区。 -当前提供 **0.1.0 正式版**,内置平台 `0.2.9`。客户端通过发行页手动下载安装更新。 +当前提供 **0.1.1 正式版**,内置平台 `0.2.9`。客户端通过发行页手动下载安装更新。 ## 下载与启动 -从 [桌面版发行页](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.0) 下载: +从 [桌面版发行页](https://github.com/DogeLakeDev/ScriptsForMinecraftServer/releases/tag/desktop-v0.1.1) 下载: -- `SFMC-Desktop-0.1.0-x64.exe`:安装向导,可以选择安装目录。 -- `SFMC-Desktop-0.1.0-x64.zip`:完整解压到一个目录,再运行 `SFMC Desktop.exe`。保留全部文件。 +- `SFMC-Desktop-0.1.1-x64.exe`:安装向导,可以选择安装目录。 +- `SFMC-Desktop-0.1.1-x64.zip`:完整解压到一个目录,再运行 `SFMC Desktop.exe`。保留全部文件。 - `SHA256SUMS.txt`:下载校验值;`release-manifest.json`:源代码提交与内置运行时版本。 - `artifact-attestation.sigstore.json`:GitHub Artifact Attestations 构建证明。 @@ -18,13 +18,13 @@ SFMC Desktop 是 Windows x64 桌面客户端,可以管理本机 SFMC 部署, 发行页同时公示 SHA256。下载后核对文件摘要: ```powershell -Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.0-x64.exe' -Algorithm SHA256 +Get-FileHash -LiteralPath '.\SFMC-Desktop-0.1.1-x64.exe' -Algorithm SHA256 ``` 安装 [GitHub CLI](https://cli.github.com/) 后,可核对构建证明、源码仓库、工作流和标签: ```powershell -gh attestation verify '.\SFMC-Desktop-0.1.0-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.0 --deny-self-hosted-runners +gh attestation verify '.\SFMC-Desktop-0.1.1-x64.exe' --repo DogeLakeDev/ScriptsForMinecraftServer --signer-workflow DogeLakeDev/ScriptsForMinecraftServer/.github/workflows/desktop-release.yml --source-ref refs/tags/desktop-v0.1.1 --deny-self-hosted-runners ``` 验证 ZIP 时替换文件名即可;添加 `--bundle '.\artifact-attestation.sigstore.json'` 可以验证下载的证明文件。需要固定到具体提交时,再加入 `--source-digest` 和 `release-manifest.json` 中的 `sourceCommit`。构建证明用于核对文件来源与摘要,不是 Windows 发布者证书。