From 7ac5a0139e8090959fea2c3f50aeaafe2c99c904 Mon Sep 17 00:00:00 2001 From: Ruslan Shakirov Date: Mon, 21 Sep 2026 00:02:52 +0400 Subject: [PATCH] Publish NuGet packages to MotusLabs GitHub Packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New publish-github-packages.yml packs the public packages and pushes them to nuget.pkg.github.com/ using the workflow GITHUB_TOKEN — tag-driven, or via workflow_dispatch with an explicit version and a dry_run rehearsal. No NuGet.org publishing. Docs describe triggering and consuming the feed. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/publish-github-packages.yml | 110 ++++++++++++++++++ DEVELOPMENT.md | 28 ++++- DEVELOPMENT_zh-cn.md | 4 +- 3 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/publish-github-packages.yml diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml new file mode 100644 index 000000000..d4951cada --- /dev/null +++ b/.github/workflows/publish-github-packages.yml @@ -0,0 +1,110 @@ +name: Publish GitHub Packages + +# Packs the public TensorSharp packages and pushes them to the repository +# owner's GitHub Packages NuGet feed (nuget.pkg.github.com/). +# +# 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 +# publishes; it accepts an explicit `version` and a `dry_run` that packs and +# verifies without pushing. +# +# 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: the workflow's own GITHUB_TOKEN with `packages: write`. +# No external feed, no OIDC, no API keys. GitHub Packages does not accept +# symbol packages, so only *.nupkg is pushed; *.snupkg remain available as +# workflow artifacts. +# +# Packages are pushed one at a time rather than by glob so a rejected package +# is visible in the log instead of hidden behind a later failure. + +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." + required: false + type: boolean + default: false + +permissions: + contents: read + packages: write + +concurrency: + # Never cancel an in-flight publish — a half-pushed release is worse than a queue. + group: publish-github-packages-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + name: Pack & publish + runs-on: ubuntu-latest + 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 + + - 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 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 6d02138df..6debfe9f9 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -552,7 +552,7 @@ The repository is split along package boundaries so consumers can depend on only 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. Until a publish channel is re-established, 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 to any feed; they ship with the next version tag. Until then, consumers of those layers need project references from a source checkout. | Project | NuGet package | Public namespace | Responsibility | |---|---|---|---| @@ -586,14 +586,34 @@ The verifier runs `dotnet pack` for the public packages above and fails if an in ### Publishing a package release (maintainers) -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: +The [`Publish GitHub Packages`](.github/workflows/publish-github-packages.yml) workflow packs the public projects above and pushes them to the organization's GitHub Packages NuGet feed. Nothing is pushed to NuGet.org from CI. Trigger it by pushing a version tag — the tag (leading `v` stripped) overrides `TensorSharpVersion` for every package, so all packages ship with a single coordinated version — or manually via `workflow_dispatch`, which accepts an explicit `version` and a `dry_run` rehearsal that packs and verifies without pushing: + +```bash +git tag vX.Y.Z.W +git push origin vX.Y.Z.W +``` + +Packing is managed-only — the native GGML/CUDA/MLX libraries are not embedded in the packages — and is gated by `eng/verify-packages.ps1 -SkipNativeBuild`. Authentication uses the workflow's own `GITHUB_TOKEN` with `packages: write`; there are no external accounts or API keys. GitHub Packages does not accept symbol packages, so only `.nupkg` files are pushed and `.snupkg` remain available as workflow artifacts. `ContinuousIntegrationBuild` is set only under GitHub Actions, so local packs keep their normal source paths. + +Packages land at `https://nuget.pkg.github.com/MotusLabs/index.json`. New packages are private by default; an org admin can make them public in the organization's Packages settings. To consume the feed, configure a NuGet source with a classic PAT that has `read:packages` (GitHub Packages' NuGet feed requires authenticated restore even for public packages): + +```bash +dotnet nuget add source \ + --username \ + --password \ + --store-password-in-clear-text \ + --name motuslabs \ + "https://nuget.pkg.github.com/MotusLabs/index.json" + +dotnet add package TensorSharp.Tensors +``` + +To validate package metadata and README dependency boundaries without publishing: ```powershell pwsh ./eng/verify-packages.ps1 ``` -`ContinuousIntegrationBuild` is set only under GitHub Actions, so local packs keep their normal source paths. - ### Platform binary release status **Status verified 2026-09-01:** [v3.3.0.0](https://github.com/zhongkaifu/TensorSharp/releases/tag/v3.3.0.0) publishes ten self-contained application archives of **TensorSharp.Server** and **TensorSharp.Cli**, including the .NET 10 runtime and native libraries. There is one Server archive and one CLI archive for each of the five platform/backend suffixes below. Check the [Releases page](https://github.com/zhongkaifu/TensorSharp/releases) for newer versions instead of assuming that this stamped version remains latest. diff --git a/DEVELOPMENT_zh-cn.md b/DEVELOPMENT_zh-cn.md index 3bc63d2ab..ecd292bab 100644 --- a/DEVELOPMENT_zh-cn.md +++ b/DEVELOPMENT_zh-cn.md @@ -362,7 +362,7 @@ TensorSharp/ 当前真正发布到 [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`——已可打包并通过校验,但尚未推送到任何源;它们将随下一个版本标签一同发布到 GitHub Packages。在此之前,使用这些层仍需从源码 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*` 标签会触发归档工作流;只有所需 job 全部成功后才会发布产物。 +- 推送 `v*` 标签会触发归档与 GitHub Packages 发布工作流;只有所需 job 全部成功后才会发布产物。 - `-cuda` 归档已内置 CUDA 运行时库(`cudart` / `cublas` / `cublasLt`),但运行时仍需 NVIDIA GPU 与兼容驱动;`-cpu` 归档可在任意机器运行。macOS 归档需 Apple Silicon。 - 如需预演,可手动触发该工作流(`workflow_dispatch`)并填写 `version` 输入——它会构建全部平台并创建**草稿** Release。可用 `cuda_arch` 输入覆盖 CUDA 构建的目标 GPU 架构。