diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index efb89ac..e9dc2e5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,7 +26,7 @@ jobs: timeout-minutes: 30 steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index fd0f0b5..28d3d20 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -21,10 +21,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -79,10 +79,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -100,10 +100,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -121,10 +121,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -147,7 +147,7 @@ jobs: - name: Upload Chaos Report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: chaos-nightly-2h-${{ runner.os }} path: | diff --git a/.github/workflows/performance-evidence.yml b/.github/workflows/performance-evidence.yml index 6198384..100c389 100644 --- a/.github/workflows/performance-evidence.yml +++ b/.github/workflows/performance-evidence.yml @@ -22,10 +22,10 @@ jobs: SHARPLINK_MATRIX_OUTPUT: ${{ github.workspace }}/artifacts/performance/smoke/${{ github.sha }} steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -42,7 +42,7 @@ jobs: - name: Upload raw smoke evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: sharplink-performance-smoke-${{ github.sha }} path: artifacts/performance/smoke/${{ github.sha }} diff --git a/.github/workflows/pr-quick.yml b/.github/workflows/pr-quick.yml index 30a7bbd..c4e29f2 100644 --- a/.github/workflows/pr-quick.yml +++ b/.github/workflows/pr-quick.yml @@ -20,10 +20,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -91,7 +91,7 @@ jobs: - name: Upload Chaos Report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: chaos-pr-smoke path: | diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml index b77370b..f746c2a 100644 --- a/.github/workflows/release-gate.yml +++ b/.github/workflows/release-gate.yml @@ -25,10 +25,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -63,7 +63,7 @@ jobs: - name: Upload Integration Diagnostics if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: integration-${{ runner.os }}-diagnostics path: artifacts/test-results/integration-${{ runner.os }} @@ -100,10 +100,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -121,10 +121,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -156,7 +156,7 @@ jobs: test -n "$(find artifacts/nuget -name '*.nupkg' -type f)" - name: Upload NuGet artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: nuget-packages path: | @@ -174,10 +174,10 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: '1' steps: - name: Checkout - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x @@ -194,7 +194,7 @@ jobs: - name: Upload Chaos Report if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: chaos-release-smoke-${{ runner.os }} path: | @@ -223,15 +223,15 @@ jobs: contents: read steps: - name: Checkout exact tag - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup .NET - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: dotnet-version: 10.0.x - name: Download release-gated packages - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: nuget-packages path: artifacts/nuget diff --git a/CHANGELOG.md b/CHANGELOG.md index c9b5140..469b693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ ## [Unreleased] +## [1.0.1] - 2026-08-02 + +### Fixed + +- A standalone contract project can now reference only `SharpLink.Sdk`. The package brings the required runtime and abstractions transitively, so generated Proxy, Stub, Codec, and Manifest sources no longer fail with missing SharpLink namespaces or types. +- Contract marker types now live in `SharpLink.Abstractions` while retaining the `SharpLink.Sdk` namespace. `SharpLink.Sdk` forwards every type published by 1.0.0, preserving existing compiled consumers without introducing a NuGet dependency cycle. + +### Changed + +- The README multi-cluster client section is now fully localized in Chinese, and the package graph, separated-contract guidance, architecture, and release order reflect the SDK-only contract reference model. +- All SharpLink NuGet packages now include the transparent SharpLink icon, which is also displayed in the README. +- Runtime and build dependencies are refreshed together: Microsoft.Extensions, System.Text.Json, and System.Threading.RateLimiting move to 10.0.10, while Microsoft.CodeAnalysis.Analyzers moves to 5.6.0. The source-generator compiler API and TUnit remain pinned to their verified SDK-compatible baselines after the proposed major updates failed the release gate. +- GitHub Actions remain pinned to exact commits while moving to checkout 7.0.1, setup-dotnet 6.0.0, upload-artifact 7.0.1, and download-artifact 8.0.1. + +### Compatibility + +- Protocol v2 bytes, generated contract IDs, runtime routing, transport behavior, and public type names are unchanged. This patch repairs package dependency closure, preserves old assembly references through type forwarding, updates release assets and documentation, and refreshes compatible build/runtime dependencies. + ## [1.0.0] - 2026-08-02 ### Highlights diff --git a/Directory.Build.props b/Directory.Build.props index 0d2c1db..7565cfc 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,11 +7,12 @@ - 1.0.0 + 1.0.1 sunsi MIT false README.md + sharplink-icon.png rpc;dotnet;source-generator;streaming;ipc;tcp;shared-memory;nativeaot https://github.com/SunSi12138/SharpLink/blob/v$(Version)/CHANGELOG.md https://github.com/SunSi12138/SharpLink @@ -46,6 +47,7 @@ + diff --git a/Directory.Packages.props b/Directory.Packages.props index 21ba259..954e930 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,29 +4,30 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive + - - + + - - + + - - - - - - + + + + + + diff --git a/README.md b/README.md index 6551bac..8f3382c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # SharpLink +

+ SharpLink 图标 +

+ [![PR Quick](https://github.com/SunSi12138/SharpLink/actions/workflows/pr-quick.yml/badge.svg)](https://github.com/SunSi12138/SharpLink/actions/workflows/pr-quick.yml) [![Nightly Regression](https://github.com/SunSi12138/SharpLink/actions/workflows/nightly.yml/badge.svg)](https://github.com/SunSi12138/SharpLink/actions/workflows/nightly.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) @@ -19,9 +23,9 @@ 核心项目(`src/`): -- `SharpLink.Abstractions`:Protocol v2 公共模型、公共接口、通道与传输抽象 +- `SharpLink.Abstractions`:契约标记、Protocol v2 公共模型、公共接口、通道与传输抽象 - `SharpLink.Runtime`:`RpcSession`、`StreamManager`、实例级 Codec Provider、传输实现与底层收发逻辑 -- `SharpLink.Sdk`:`IService`、`[RpcContract]`、`[RpcService]`、`[Oneway]`、`[Timeout]` 等契约标记 +- `SharpLink.Sdk`:契约项目的单一引用入口,提供分析器、源生成器及 1.0.0 类型转发兼容层 - `SharpLink.Client`:客户端 Builder、连接生命周期、请求管理与代理调用通道 - `SharpLink.Server`:服务端 Builder、连接管理、Stub 分发、心跳与取消处理 - `SharpLink.Hosting`:`IServiceCollection` 扩展与 HostedService 集成 @@ -99,22 +103,18 @@ dotnet run --project demo/SeparatedClient/SeparatedClient.csproj [assembly: SharpLinkRpcContracts(typeof(MyContract1), typeof(MyContract2))] ``` -## Multi-cluster clients +## 多集群客户端 -`SharpLinkMultiClusterClientBuilder` owns several isolated child clients. A contract is mapped to a -single child while its proxy is created; RPC calls made through that proxy do not consult a -coordinator, cluster name, or per-call routing context. +`SharpLinkMultiClusterClientBuilder` 管理多个彼此隔离的子客户端。创建代理时,每个契约只映射到一个子客户端;之后通过该代理发起 RPC 调用时,不会再查询协调器、集群名称或逐调用路由上下文。 -Declare static contract-assembly routes in the application or host assembly, not in a reusable -contract package: +静态契约程序集路由应声明在应用或宿主程序集中,不要写入可复用的契约包: ```csharp [assembly: SharpLinkClusterContractAssembly("orders", typeof(OrderContractsMarker))] [assembly: SharpLinkClusterContractAssembly("payments", typeof(PaymentContractsMarker))] ``` -Configure each slot with the existing child builder API. The `UseCluster` method inside the delegate -still configures endpoint topology for that one slot; it is not the multi-cluster coordinator API. +每个槽位继续使用现有的子 Builder API 配置。委托内的 `UseCluster` 仍然只负责该槽位自身的端点拓扑,并不是多集群协调器 API。 ```csharp var client = SharpLinkMultiClusterClientBuilder.Create() @@ -127,18 +127,14 @@ var orders = client.Get(); var payments = client.Get(); ``` -Every slot is required by default. A slot intentionally reserved for plugins must explicitly opt in: +默认情况下,每个槽位都必须具有契约。专门预留给插件的槽位必须显式启用动态契约: ```csharp .AddCluster("plugins", child => child.UseTcp("127.0.0.1", 5103), slot => slot.AllowDynamicContracts = true) ``` -Dynamic contracts are registered against an explicit slot with -`RegisterAssembly(cluster, assembly)`, `UnregisterAssemblyAsync(cluster, assembly, timeout)`, and -`ReplaceAssemblyAsync(cluster, oldAssembly, newAssembly, timeout)`. There is no default cluster, -per-call cluster override, cross-cluster retry, or cluster identifier on the wire. See -[`doc/dynamic-modules-and-multicluster.md`](doc/dynamic-modules-and-multicluster.md) for lifecycle and migration details. +动态契约必须注册到明确指定的槽位,相关 API 为 `RegisterAssembly(cluster, assembly)`、`UnregisterAssemblyAsync(cluster, assembly, timeout)` 和 `ReplaceAssemblyAsync(cluster, oldAssembly, newAssembly, timeout)`。当前不存在默认集群、逐调用集群覆盖、跨集群重试,线上协议也不携带集群标识。生命周期与迁移细节见 [`doc/dynamic-modules-and-multicluster.md`](doc/dynamic-modules-and-multicluster.md)。 ## 契约 Manifest 与兼容性基线 diff --git a/assets/sharplink-icon.png b/assets/sharplink-icon.png new file mode 100644 index 0000000..c3e97c4 Binary files /dev/null and b/assets/sharplink-icon.png differ diff --git a/doc/architecture.md b/doc/architecture.md index 81c39ac..deef5db 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -8,11 +8,14 @@ Application -> SharpLink.Runtime -> SharpLink.Abstractions -SharpLink.Sdk - -> IService / RpcContract / RpcService / Oneway / Timeout / SharpLinkCallOptions +Contract project + -> SharpLink.Sdk + -> SharpLink.Runtime + -> SharpLink.Abstractions -SharpLink.Generator - -> 扫描契约与服务,生成 Proxy / Stub / Codec / Assembly Manifest +SharpLink.Sdk + -> SharpLink.Generator(Analyzer) + -> 1.0.0 契约类型转发兼容层 SharpLink.Serializer.SharpPack -> 声明通用 Codec Adapter registration,并为复杂对象图提供 manifest-scoped SharpPack Context @@ -21,6 +24,7 @@ SharpLink.Serializer.SharpPack ## 各模块职责 - `SharpLink.Abstractions` + - 保持 `SharpLink.Sdk` 命名空间的契约标记(`IService` / `RpcContract` / `RpcService` / `Oneway` / `Timeout` / `SharpLinkCallOptions`) - Protocol v2 模型(`ProtocolV2FrameType` / `ProtocolV2FrameFlags` / `ProtocolV2Constants`) - 核心抽象(`IRpcChannel`、`IRpcStub`、`IClientTransportFactory`、`IServerTransportListener`、`ITransportConnection`、`IRpcSession`、`IRpcCodec`) - 结构化错误模型(`SharpLinkException` / `SharpLinkErrorCode`) @@ -33,8 +37,10 @@ SharpLink.Serializer.SharpPack - Protocol v2 帧编解码、发送泵、池化缓冲与并发容器 - `SharpLink.Sdk` - - 只承载契约层标记和最小公共类型 - - 当前不承载 Builder;Builder 位于 `SharpLink.Client` 和 `SharpLink.Server` + - 作为契约项目的单一包引用入口,传递引入 Runtime 和 Abstractions + - 携带编译期 Analyzer 与 Source Generator + - 将 1.0.0 发布过的契约类型转发到 Abstractions,保持旧二进制引用兼容 + - 不承载 Builder;Builder 位于 `SharpLink.Client` 和 `SharpLink.Server` - `SharpLink.Client` - `SharpClientBuilder` diff --git a/doc/getting-started.md b/doc/getting-started.md index 049220e..a7b63eb 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -60,7 +60,7 @@ Client 和 Server 都是异步可释放对象。生产代码必须在停止时 ## 分离部署 -推荐把契约放在独立程序集,由 Client 和 Server 共同引用。契约程序集也必须引用 SDK、Abstractions、Runtime 和 Generator,因为生成代理、Stub 与 Manifest 依赖这些类型。完整结构见: +推荐把契约放在独立程序集,由 Client 和 Server 共同引用。契约程序集只需引用 `SharpLink.Sdk`;SDK 会传递引入生成 Proxy、Stub、Codec 与 Manifest 所需的 Runtime 和 Abstractions,并自动携带 Source Generator。Client 和 Server 项目再分别引用契约程序集及自身所需的 `SharpLink.Client` 或 `SharpLink.Server` 包。完整结构见: - `demo/SeparatedContracts` - `demo/SeparatedServer` diff --git a/doc/index.md b/doc/index.md index a77d26e..fbb1578 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,6 +1,6 @@ # SharpLink 文档 -本文档集以当前源码和 `1.0.0` 稳定版为准,不再按 0.x 小版本保存用户文档。公开 API 的精确参数、返回值和异常语义同时通过 NuGet 包内 XML 文档提供。 +本文档集以当前源码和 `1.0.1` 稳定版为准,不再按 0.x 小版本保存用户文档。公开 API 的精确参数、返回值和异常语义同时通过 NuGet 包内 XML 文档提供。 ## 学习路径 diff --git a/doc/releasing.md b/doc/releasing.md index 452e3dd..b0a970d 100644 --- a/doc/releasing.md +++ b/doc/releasing.md @@ -4,7 +4,7 @@ ## 版本与兼容性 -- NuGet 包版本由 `VersionPrefix` 和可选的 `VersionSuffix` 组成。稳定版 `1.0.0` 不设置后缀;预发布版本可使用例如 `rc7` 的后缀。 +- NuGet 包版本由 `VersionPrefix` 和可选的 `VersionSuffix` 组成。稳定版不设置后缀;预发布版本可使用例如 `rc7` 的后缀。 - `AssemblyVersion` 与 `FileVersion` 始终使用四段纯数字;预发布后缀只进入包版本和 `InformationalVersion`。 - 冻结前更新 `CHANGELOG.md`、包引用示例和迁移说明。公开 API、Protocol v2、生成代码、契约 Manifest 或默认行为的变化必须明确标注兼容性。 - `1.0.0` 发布后保留其公开 API 包作为后续 `PackageValidationBaselineVersion`;不在补丁版本中进行破坏性 API 或 wire 变更。 @@ -47,7 +47,7 @@ ## 发布与回滚 -`release-gate.yml` 只在 `v*` 标签触发、全部三平台测试/AOT/包安装/Chaos 门禁通过后进入受保护的 `release` Environment。发布 job 下载同一次运行产出的 `.nupkg` 和 `.snupkg`,使用 NuGet.org OIDC Trusted Publishing,不保存长期 API key;手工触发 Release Gate 只验证,不发布。推送前再次校验每个包的 ID、版本、SHA 和符号包配对关系,并按 `Sdk → Abstractions → Runtime/Serializer → Client/Server → Hosting` 顺序发布。 +`release-gate.yml` 只在 `v*` 标签触发、全部三平台测试/AOT/包安装/Chaos 门禁通过后进入受保护的 `release` Environment。发布 job 下载同一次运行产出的 `.nupkg` 和 `.snupkg`,使用 NuGet.org OIDC Trusted Publishing,不保存长期 API key;手工触发 Release Gate 只验证,不发布。推送前再次校验每个包的 ID、版本、SHA 和符号包配对关系,并按 `Abstractions → Runtime → Sdk/Serializer → Client/Server → Hosting` 顺序发布。 NuGet 包不可覆盖或删除来替代修复。若发布内容有缺陷: diff --git a/eng/verify-packages.sh b/eng/verify-packages.sh index 3f44620..70348d3 100755 --- a/eng/verify-packages.sh +++ b/eng/verify-packages.sh @@ -48,11 +48,20 @@ for package_id in "${PACKAGES[@]}"; do unzip -Z1 "$nupkg" | grep -Fx "lib/net10.0/$package_id.dll" >/dev/null unzip -Z1 "$nupkg" | grep -Fx "lib/net10.0/$package_id.xml" >/dev/null + unzip -Z1 "$nupkg" | grep -Fx "sharplink-icon.png" >/dev/null unzip -Z1 "$snupkg" | grep -Fx "lib/net10.0/$package_id.pdb" >/dev/null unzip -p "$nupkg" "$package_id.nuspec" | grep -F "commit=\"$EXPECTED_COMMIT\"" >/dev/null done unzip -Z1 "$ARTIFACT_DIR/SharpLink.Sdk.$EXPECTED_VERSION.nupkg" | grep -Fx "analyzers/dotnet/cs/SharpLink.Generator.dll" >/dev/null +unzip -p "$ARTIFACT_DIR/SharpLink.Sdk.$EXPECTED_VERSION.nupkg" SharpLink.Sdk.nuspec | + grep -F "/dev/null + +if unzip -p "$ARTIFACT_DIR/SharpLink.Abstractions.$EXPECTED_VERSION.nupkg" SharpLink.Abstractions.nuspec | + grep -F '/dev/null; then + echo "SharpLink.Abstractions must not depend on SharpLink.Sdk." >&2 + exit 1 +fi echo "Verified ${#PACKAGES[@]} package and symbol pairs for $EXPECTED_VERSION at $EXPECTED_COMMIT." diff --git a/src/SharpLink.Sdk/IService.cs b/src/SharpLink.Abstractions/Sdk/IService.cs similarity index 100% rename from src/SharpLink.Sdk/IService.cs rename to src/SharpLink.Abstractions/Sdk/IService.cs diff --git a/src/SharpLink.Sdk/IdempotentAttribute.cs b/src/SharpLink.Abstractions/Sdk/IdempotentAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/IdempotentAttribute.cs rename to src/SharpLink.Abstractions/Sdk/IdempotentAttribute.cs diff --git a/src/SharpLink.Sdk/NonCancellableAttribute.cs b/src/SharpLink.Abstractions/Sdk/NonCancellableAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/NonCancellableAttribute.cs rename to src/SharpLink.Abstractions/Sdk/NonCancellableAttribute.cs diff --git a/src/SharpLink.Sdk/OnewayAttribute.cs b/src/SharpLink.Abstractions/Sdk/OnewayAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/OnewayAttribute.cs rename to src/SharpLink.Abstractions/Sdk/OnewayAttribute.cs diff --git a/src/SharpLink.Sdk/RpcCodecAdapterAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcCodecAdapterAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcCodecAdapterAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcCodecAdapterAttribute.cs diff --git a/src/SharpLink.Sdk/RpcCodecAdapterRegistrationAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcCodecAdapterRegistrationAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcCodecAdapterRegistrationAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcCodecAdapterRegistrationAttribute.cs diff --git a/src/SharpLink.Sdk/RpcContractAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcContractAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcContractAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcContractAttribute.cs diff --git a/src/SharpLink.Sdk/RpcIgnoreAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcIgnoreAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcIgnoreAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcIgnoreAttribute.cs diff --git a/src/SharpLink.Sdk/RpcMemberAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcMemberAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcMemberAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcMemberAttribute.cs diff --git a/src/SharpLink.Sdk/RpcRequiredAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcRequiredAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcRequiredAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcRequiredAttribute.cs diff --git a/src/SharpLink.Sdk/RpcSerializableAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcSerializableAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcSerializableAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcSerializableAttribute.cs diff --git a/src/SharpLink.Sdk/RpcServiceAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcServiceAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcServiceAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcServiceAttribute.cs diff --git a/src/SharpLink.Sdk/RpcUnionCaseAttribute.cs b/src/SharpLink.Abstractions/Sdk/RpcUnionCaseAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/RpcUnionCaseAttribute.cs rename to src/SharpLink.Abstractions/Sdk/RpcUnionCaseAttribute.cs diff --git a/src/SharpLink.Sdk/SharpLinkCallOptions.cs b/src/SharpLink.Abstractions/Sdk/SharpLinkCallOptions.cs similarity index 100% rename from src/SharpLink.Sdk/SharpLinkCallOptions.cs rename to src/SharpLink.Abstractions/Sdk/SharpLinkCallOptions.cs diff --git a/src/SharpLink.Sdk/SharpLinkClusterContractAssemblyAttribute.cs b/src/SharpLink.Abstractions/Sdk/SharpLinkClusterContractAssemblyAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/SharpLinkClusterContractAssemblyAttribute.cs rename to src/SharpLink.Abstractions/Sdk/SharpLinkClusterContractAssemblyAttribute.cs diff --git a/src/SharpLink.Sdk/SharpLinkMetadata.cs b/src/SharpLink.Abstractions/Sdk/SharpLinkMetadata.cs similarity index 100% rename from src/SharpLink.Sdk/SharpLinkMetadata.cs rename to src/SharpLink.Abstractions/Sdk/SharpLinkMetadata.cs diff --git a/src/SharpLink.Sdk/SharpLinkRpcContractsAttribute.cs b/src/SharpLink.Abstractions/Sdk/SharpLinkRpcContractsAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/SharpLinkRpcContractsAttribute.cs rename to src/SharpLink.Abstractions/Sdk/SharpLinkRpcContractsAttribute.cs diff --git a/src/SharpLink.Sdk/SharpLinkServiceLifetime.cs b/src/SharpLink.Abstractions/Sdk/SharpLinkServiceLifetime.cs similarity index 100% rename from src/SharpLink.Sdk/SharpLinkServiceLifetime.cs rename to src/SharpLink.Abstractions/Sdk/SharpLinkServiceLifetime.cs diff --git a/src/SharpLink.Sdk/TimeoutAttribute.cs b/src/SharpLink.Abstractions/Sdk/TimeoutAttribute.cs similarity index 100% rename from src/SharpLink.Sdk/TimeoutAttribute.cs rename to src/SharpLink.Abstractions/Sdk/TimeoutAttribute.cs diff --git a/src/SharpLink.Abstractions/SharpLink.Abstractions.csproj b/src/SharpLink.Abstractions/SharpLink.Abstractions.csproj index 5e5581d..6361784 100644 --- a/src/SharpLink.Abstractions/SharpLink.Abstractions.csproj +++ b/src/SharpLink.Abstractions/SharpLink.Abstractions.csproj @@ -3,6 +3,6 @@ - + diff --git a/src/SharpLink.Sdk/SharpLink.Sdk.csproj b/src/SharpLink.Sdk/SharpLink.Sdk.csproj index 0920400..21aaca9 100644 --- a/src/SharpLink.Sdk/SharpLink.Sdk.csproj +++ b/src/SharpLink.Sdk/SharpLink.Sdk.csproj @@ -1,5 +1,6 @@ + + + + + diff --git a/test/SharpLink.SdkOnlyPackageSmoke/EchoContract.cs b/test/SharpLink.SdkOnlyPackageSmoke/EchoContract.cs new file mode 100644 index 0000000..a7d6908 --- /dev/null +++ b/test/SharpLink.SdkOnlyPackageSmoke/EchoContract.cs @@ -0,0 +1,9 @@ +using SharpLink.Sdk; + +namespace SharpLink.SdkOnlyPackageSmoke; + +[RpcContract] +public interface IEchoService : IService +{ + ValueTask EchoAsync(string value, CancellationToken cancellationToken); +} diff --git a/test/SharpLink.SdkOnlyPackageSmoke/SharpLink.SdkOnlyPackageSmoke.csproj b/test/SharpLink.SdkOnlyPackageSmoke/SharpLink.SdkOnlyPackageSmoke.csproj new file mode 100644 index 0000000..a478b1a --- /dev/null +++ b/test/SharpLink.SdkOnlyPackageSmoke/SharpLink.SdkOnlyPackageSmoke.csproj @@ -0,0 +1,14 @@ + + + net10.0 + enable + enable + false + $(VersionPrefix) + $(VersionPrefix)-$(VersionSuffix) + + + + + + diff --git a/test/SharpLink.UnitTests/SdkTypeForwardingTests.cs b/test/SharpLink.UnitTests/SdkTypeForwardingTests.cs new file mode 100644 index 0000000..921ba57 --- /dev/null +++ b/test/SharpLink.UnitTests/SdkTypeForwardingTests.cs @@ -0,0 +1,59 @@ +using System.Linq; + +namespace SharpLink.UnitTests; + +public class SdkTypeForwardingTests +{ + private static readonly string[] PublishedContractTypes = + [ + "SharpLink.Sdk.IdempotentAttribute", + "SharpLink.Sdk.IService", + "SharpLink.Sdk.NonCancellableAttribute", + "SharpLink.Sdk.OnewayAttribute", + "SharpLink.Sdk.RpcCodecAdapterAttribute", + "SharpLink.Sdk.RpcCodecAdapterRegistrationAttribute", + "SharpLink.Sdk.RpcContractAttribute", + "SharpLink.Sdk.RpcIgnoreAttribute", + "SharpLink.Sdk.RpcMemberAttribute", + "SharpLink.Sdk.RpcRequiredAttribute", + "SharpLink.Sdk.RpcSerializableAttribute", + "SharpLink.Sdk.RpcServiceAttribute", + "SharpLink.Sdk.RpcUnionCaseAttribute", + "SharpLink.Sdk.SharpLinkCallOptions", + "SharpLink.Sdk.SharpLinkClusterContractAssemblyAttribute", + "SharpLink.Sdk.SharpLinkMetadata", + "SharpLink.Sdk.SharpLinkRpcContractsAttribute", + "SharpLink.Sdk.SharpLinkServiceLifetime", + "SharpLink.Sdk.TimeoutAttribute" + ]; + + [Test] + public void SdkAssemblyShouldForwardEveryPublishedContractTypeToAbstractions() + { + var sdkAssembly = System.Reflection.Assembly.Load("SharpLink.Sdk"); + var forwardedTypes = sdkAssembly.GetForwardedTypes() + .ToDictionary(type => type.FullName!, StringComparer.Ordinal); + + Ensure(forwardedTypes.Count == PublishedContractTypes.Length, + "SDK should expose exactly the contract types published in 1.0.0 as forwarders"); + + foreach (var typeName in PublishedContractTypes) + { + if (!forwardedTypes.TryGetValue(typeName, out var forwardedType)) + { + throw new InvalidOperationException($"SDK should forward {typeName}"); + } + + Ensure(forwardedType.Assembly.GetName().Name == "SharpLink.Abstractions", + $"{typeName} should resolve from SharpLink.Abstractions"); + } + } + + private static void Ensure(bool condition, string message) + { + if (!condition) + { + throw new InvalidOperationException(message); + } + } +} diff --git a/test/SharpLink.UnitTests/SharpLink.UnitTests.csproj b/test/SharpLink.UnitTests/SharpLink.UnitTests.csproj index e153711..c0fb15f 100644 --- a/test/SharpLink.UnitTests/SharpLink.UnitTests.csproj +++ b/test/SharpLink.UnitTests/SharpLink.UnitTests.csproj @@ -8,6 +8,7 @@ +