diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml deleted file mode 100644 index 6a6f9b09a..000000000 --- a/.github/workflows/publish-nuget.yml +++ /dev/null @@ -1,174 +0,0 @@ -name: Publish NuGet - -# Packs the public TensorSharp packages and pushes them to NuGet.org and the -# repository's GitHub Packages feed. -# -# Trigger: push a version tag like `v2.8.6`. The tag (with the leading `v` -# stripped) becomes the package version, overriding the default in -# Directory.Build.props. `workflow_dispatch` is also available for manual / -# dry-run publishes. -# -# Packing is managed-only: the native libraries (GGML/CUDA/MLX) are not embedded -# in the .nupkg, so this job skips the native build (-SkipNativeBuild) and runs -# on a stock GitHub-hosted runner with no GPU/CMake toolchain. -# -# Authentication — nuget.org uses Trusted Publishing (OIDC), so there is no -# long-lived API key to store or rotate. NuGet/login exchanges a GitHub OIDC -# token for an API key valid for 1 hour, which is why that step sits immediately -# before the push rather than at the top of the job. -# -# The nuget.org policy pins all four of these; changing any one here without -# updating the policy at https://www.nuget.org/account/trustedpublishing (and -# vice versa) breaks the token exchange with a 4xx from the token service: -# - Repository owner : zhongkaifu (id 3347714) -# - Repository : TensorSharp (id 1200226423) -# - Workflow file : publish-nuget.yml (renaming this file breaks it) -# - Environment : production (the `environment:` key on the job below; -# the OIDC token only carries an environment claim when -# the job declares one, so removing it breaks the match) -# -# GITHUB_TOKEN (provided automatically) covers the GitHub Packages push and -# needs `packages: write`; `id-token: write` is what lets the job mint the OIDC -# token for nuget.org. -# -# Packages are pushed one at a time rather than by glob so that one rejected -# package id (e.g. an id owned by someone else on nuget.org, which returns 403 -# and is NOT covered by --skip-duplicate) does not hide the fate of the others. - -on: - push: - tags: - - "v*" - workflow_dispatch: - inputs: - version: - description: "Package version to publish (e.g. 2.8.6). Required when not run from a tag." - required: false - default: "" - dry_run: - description: "Pack and verify only; do not push to any feed." - required: false - type: boolean - default: false - -permissions: - contents: read - packages: write - # Required for Trusted Publishing: lets the job request a GitHub OIDC token - # to exchange for a short-lived nuget.org API key. - id-token: write - -concurrency: - # Never cancel an in-flight publish — a half-pushed release is worse than a queue. - group: publish-nuget-${{ github.ref }} - cancel-in-progress: false - -jobs: - publish: - name: Pack & publish - runs-on: ubuntu-latest - # Must stay in sync with the Environment field of the nuget.org trusted - # publishing policy — this is what puts the `environment` claim in the OIDC - # token that the policy matches on. - environment: production - timeout-minutes: 20 - steps: - - uses: actions/checkout@v4 - with: - # Native libraries are not packed, so the ggml sources are not needed. - submodules: false - - - uses: actions/setup-dotnet@v4 - with: - dotnet-version: "10.0.x" - - - name: Resolve package version - id: ver - shell: bash - run: | - set -euo pipefail - V="${{ github.event.inputs.version }}" - if [ -z "$V" ] && [ "${{ github.ref_type }}" = "tag" ]; then - V="${GITHUB_REF_NAME#v}" - fi - if [ -z "$V" ]; then - echo "::error::No version resolved. Push a tag like v2.8.6 or supply the 'version' input." >&2 - exit 1 - fi - echo "version=$V" >> "$GITHUB_OUTPUT" - echo "Resolved package version: $V" - - - name: Pack & verify packages - shell: pwsh - run: >- - ./eng/verify-packages.ps1 - -PackageVersion '${{ steps.ver.outputs.version }}' - -SkipNativeBuild - -OutputDirectory artifacts/packages - - - name: Upload package artifacts - uses: actions/upload-artifact@v4 - with: - name: nuget-packages-${{ steps.ver.outputs.version }} - path: | - artifacts/packages/*.nupkg - artifacts/packages/*.snupkg - retention-days: 30 - - # Trusted Publishing: exchanges this job's OIDC token for an API key that - # expires in 1 hour. Runs before any push so that a policy mismatch fails - # the release before a single package ships to either feed, rather than - # leaving GitHub Packages updated and nuget.org untouched. A failure here - # means the OIDC claims did not match the nuget.org policy - # (owner / repo / workflow file name / environment). - - name: NuGet login (OIDC) - id: login - if: ${{ !inputs.dry_run }} - uses: NuGet/login@v1 - with: - user: monkeyfu - - - name: Push to GitHub Packages - if: ${{ !inputs.dry_run }} - shell: bash - run: | - set -euo pipefail - # GitHub Packages does not accept symbol packages, so only *.nupkg here. - for pkg in artifacts/packages/*.nupkg; do - echo "::group::$(basename "$pkg")" - dotnet nuget push "$pkg" \ - --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ - --api-key "${{ secrets.GITHUB_TOKEN }}" \ - --skip-duplicate - echo "::endgroup::" - done - - - name: Push to NuGet.org - if: ${{ !inputs.dry_run }} - env: - NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY }} - shell: bash - run: | - set -uo pipefail - failed=() - # Symbol packages follow their .nupkg; both use --skip-duplicate so a - # re-run over an already-published version is a no-op rather than an error. - for pkg in artifacts/packages/*.nupkg artifacts/packages/*.snupkg; do - [ -e "$pkg" ] || continue - echo "::group::$(basename "$pkg")" - if ! dotnet nuget push "$pkg" \ - --source "https://api.nuget.org/v3/index.json" \ - --api-key "$NUGET_API_KEY" \ - --skip-duplicate; then - failed+=("$(basename "$pkg")") - echo "::error::Failed to push $(basename "$pkg") to nuget.org." - fi - echo "::endgroup::" - done - - if [ ${#failed[@]} -gt 0 ]; then - echo "::error::${#failed[@]} package(s) were rejected by nuget.org: ${failed[*]}" >&2 - echo "A 403 here usually means the package id is registered to a different nuget.org account." >&2 - exit 1 - fi - echo "All packages published to nuget.org." diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml index 7f35720c9..6484ff336 100644 --- a/.github/workflows/release-binaries.yml +++ b/.github/workflows/release-binaries.yml @@ -5,10 +5,10 @@ name: Release Binaries # (TensorSharp.Server.Host and TensorSharp.Cli) for each supported platform and # attaches them to a GitHub Release. # -# Unlike `publish-nuget.yml` (which packs managed-only NuGet packages and skips -# the native build), this workflow builds the native libraries on each OS and -# bundles them into the published apps, so each archive is runnable without a -# .NET install or a separate native build: +# Unlike the managed-only NuGet packages (which are packed without any native +# build), this workflow builds the native libraries on each OS and bundles them +# into the published apps, so each archive is runnable without a .NET install +# or a separate native build: # # - win-x64-cpu GgmlOps.dll (CPU) -> .zip # - win-x64-cuda GgmlOps.dll (+CUDA) + PTX + CUDA redist -> .zip diff --git a/.github/workflows/test-matrix.yml b/.github/workflows/test-matrix.yml index 614b2a56a..1f31990d2 100644 --- a/.github/workflows/test-matrix.yml +++ b/.github/workflows/test-matrix.yml @@ -162,7 +162,10 @@ jobs: env: HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | - python3 -m pip install --user -q -U huggingface_hub requests + # PEP 668: externally-managed Pythons (Debian 12+/Ubuntu 23.04+) + # reject pip --user unless --break-system-packages is set. + python3 -m pip install --user -q -U huggingface_hub requests || \ + python3 -m pip install --user -q -U --break-system-packages huggingface_hub requests python3 benchmarks/engine_comparison/download_models.py \ --config benchmarks/engine_comparison/benchmark_config_ci.json \ --models "$BENCH_MODELS" diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 839aebb7f..6d02138df 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -548,11 +548,11 @@ TensorSharp/ The repository is split along package boundaries so consumers can depend on only the layers they actually need. -**Status verified 2026-09-08.** The publish set is **thirteen** packages — every row of the table below. `eng/verify-packages.ps1` is the authoritative list, and it gates the publish workflow, so adding a `ProjectReference` between two packable projects without updating that script fails the release. +**Status verified 2026-09-08.** The publish set is **thirteen** packages — every row of the table below. `eng/verify-packages.ps1` is the authoritative list, so adding a `ProjectReference` between two packable projects without updating that script fails the pack verification. What is on [NuGet.org](https://www.nuget.org/profiles/TensorSharp) today is a subset: **eight** ids at version **3.1.2**, published 2026-07-21 — `TensorSharp.Tensors`, `TensorSharp.Runtime`, `TensorSharp.Models`, `TensorSharp.Backends.GGML`, `TensorSharp.Backends.Cuda`, `TensorSharp.Backends.MLX`, `TensorSharp.Server`, and `TensorSharp.Cli`. Those packages lag the current source and the v3.3.0.0 application release; in particular the published `TensorSharp.Server` predates the logging and chat splits, so it does not match the layering described here. -The remaining five — `TensorSharp.Runtime.Logging`, `TensorSharp.AgentHost`, `TensorSharp.Chat`, `TensorSharp.Server.Host`, and `TensorSharp.Distributed` — are packable and verified but have never been pushed; they ship with the next version tag. Until then, consumers of those layers need project references from a source checkout. +The remaining five — `TensorSharp.Runtime.Logging`, `TensorSharp.AgentHost`, `TensorSharp.Chat`, `TensorSharp.Server.Host`, and `TensorSharp.Distributed` — are packable and verified but have never been pushed. Until a publish channel is re-established, consumers of those layers need project references from a source checkout. | Project | NuGet package | Public namespace | Responsibility | |---|---|---|---| @@ -586,19 +586,13 @@ The verifier runs `dotnet pack` for the public packages above and fails if an in ### Publishing a package release (maintainers) -The [`Publish NuGet`](.github/workflows/publish-nuget.yml) workflow packs the public projects above on a version tag and pushes them to NuGet.org and GitHub Packages. This describes the release process, not current package availability: +CI no longer publishes NuGet packages: the `publish-nuget.yml` workflow was removed, so version tags trigger only the binary release workflow and nothing is pushed to NuGet.org or GitHub Packages. To pack the public packages locally — managed-only, the native GGML/CUDA/MLX libraries are not embedded — and validate metadata and dependency boundaries: -```bash -git tag vX.Y.Z.W # the tag drives package version X.Y.Z.W -git push origin vX.Y.Z.W +```powershell +pwsh ./eng/verify-packages.ps1 ``` -- The tag (with the leading `v` stripped) overrides `TensorSharpVersion` for every package, so all packages ship with a single coordinated version. You do not need to edit `Directory.Build.props` first. -- Packing is managed-only — the native GGML/CUDA/MLX libraries are not embedded in the packages — so the workflow runs on a stock runner with `eng/verify-packages.ps1 -SkipNativeBuild` (which also sets `TensorSharpSkipGgmlNative=true` / `TensorSharpSkipMlxNative=true`). -- NuGet.org publishing uses [Trusted Publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing) (OIDC) — there is no API key secret to manage. `NuGet/login@v1` exchanges the job's GitHub OIDC token for a key valid for one hour. The policy on nuget.org pins the repository owner, repository, workflow **file name** (`publish-nuget.yml`), and the `production` environment, so renaming this workflow file, or removing `environment: production` from the job, breaks publishing until the policy is updated to match. -- Packages are pushed individually rather than by glob: a package id owned by a different NuGet.org account returns 403, which `--skip-duplicate` does *not* absorb, so the run reports exactly which packages were rejected instead of stopping at the first one. -- Packages ship with Source Link and a companion `.snupkg` symbol package. `ContinuousIntegrationBuild` is set only under GitHub Actions, so local packs keep their normal source paths. -- To rehearse without publishing, run the workflow manually (`workflow_dispatch`) with a `version` input and `dry_run` checked — it packs, verifies, and uploads the `.nupkg` files as a build artifact without pushing. +`ContinuousIntegrationBuild` is set only under GitHub Actions, so local packs keep their normal source paths. ### Platform binary release status diff --git a/DEVELOPMENT_zh-cn.md b/DEVELOPMENT_zh-cn.md index ba1b04531..3bc63d2ab 100644 --- a/DEVELOPMENT_zh-cn.md +++ b/DEVELOPMENT_zh-cn.md @@ -358,11 +358,11 @@ TensorSharp/ 仓库按包边界拆成独立层,使用者可以只引用真正需要的部分。 -**状态核验于 2026-09-08。** 发布集合共 **13** 个包,即下表的每一行。`eng/verify-packages.ps1` 是这份清单的权威来源,并且它是发布流水线的门禁:在两个可打包项目之间新增 `ProjectReference` 而不同步更新该脚本,会直接让发布失败。 +**状态核验于 2026-09-08。** 发布集合共 **13** 个包,即下表的每一行。`eng/verify-packages.ps1` 是这份清单的权威来源:在两个可打包项目之间新增 `ProjectReference` 而不同步更新该脚本,会直接让打包校验失败。 当前真正发布到 [NuGet.org](https://www.nuget.org/profiles/TensorSharp) 的只是其中一个子集:**8** 个包 ID,版本 **3.1.2**,发布于 2026-07-21——`TensorSharp.Tensors`、`TensorSharp.Runtime`、`TensorSharp.Models`、`TensorSharp.Backends.GGML`、`TensorSharp.Backends.Cuda`、`TensorSharp.Backends.MLX`、`TensorSharp.Server` 与 `TensorSharp.Cli`。它们落后于当前源码与 v3.3.0.0 应用发行版;尤其是已发布的 `TensorSharp.Server` 早于日志层与聊天层拆分,与这里描述的分层并不一致。 -其余 5 个——`TensorSharp.Runtime.Logging`、`TensorSharp.AgentHost`、`TensorSharp.Chat`、`TensorSharp.Server.Host` 与 `TensorSharp.Distributed`——已可打包并通过校验,但尚未推送,将随下一个版本标签一同发布。在此之前,使用这些层仍需从源码 checkout 添加项目引用。 +其余 5 个——`TensorSharp.Runtime.Logging`、`TensorSharp.AgentHost`、`TensorSharp.Chat`、`TensorSharp.Server.Host` 与 `TensorSharp.Distributed`——已可打包并通过校验,但尚未推送。在发布渠道恢复之前,使用这些层仍需从源码 checkout 添加项目引用。 | 项目 | NuGet 包 | 对外 namespace | 职责 | |---|---|---|---| @@ -406,7 +406,7 @@ v3.3.0.0 已发布的归档矩阵如下: | `linux-x64-cuda` | GGML CUDA + 纯 C# CUDA(PTX)+ CUDA 12.x 运行时 | `.tar.gz` | | `osx-arm64` | GGML Metal + MLX | `.tar.gz` | -- 推送 `v*` 标签会触发归档与 NuGet 工作流;只有所需 job 全部成功后才会发布产物。 +- 推送 `v*` 标签会触发归档工作流;只有所需 job 全部成功后才会发布产物。 - `-cuda` 归档已内置 CUDA 运行时库(`cudart` / `cublas` / `cublasLt`),但运行时仍需 NVIDIA GPU 与兼容驱动;`-cpu` 归档可在任意机器运行。macOS 归档需 Apple Silicon。 - 如需预演,可手动触发该工作流(`workflow_dispatch`)并填写 `version` 输入——它会构建全部平台并创建**草稿** Release。可用 `cuda_arch` 输入覆盖 CUDA 构建的目标 GPU 架构。