# Copyright (c) 2025-2026 GeWuYou # SPDX-License-Identifier: Apache-2.0 # 发布工作流(NuGet + GitHub Packages + GitHub Release) # # 功能:当推送标签时自动构建、打包,并将相同产物并发发布到 NuGet.org 与 GitHub Packages, # 最后创建 GitHub Release。 # 触发条件:推送任何标签(如 v1.0.0 或 1.0.0) # 权限:允许写入内容、包和使用 OIDC 身份验证 name: Publish (NuGet + GitHub Packages + GitHub Release) on: push: tags: - '*' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false permissions: contents: write packages: write id-token: write jobs: build-pack: name: Build And Pack runs-on: ubuntu-latest permissions: contents: read packages: read id-token: write outputs: package_version: ${{ steps.tag_version.outputs.version }} steps: - name: Checkout repository (at tag) uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: true - name: Setup .NET uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - name: Cache NuGet packages uses: actions/cache@v5 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} - name: Restore dependencies run: dotnet restore GFramework.sln # 从 GitHub 引用中提取标签版本。 # 提取逻辑:去除 refs/tags/ 前缀,然后去除 v/V 前缀。 - name: Determine tag version id: tag_version run: | set -e echo "GITHUB_REF = ${GITHUB_REF}" TAG=${GITHUB_REF#refs/tags/} VERSION=${TAG#v} VERSION=${VERSION#V} echo "tag='$TAG' -> version='$VERSION'" echo "version=$VERSION" >> "$GITHUB_OUTPUT" - name: Pack (use tag version) run: | set -e echo "Packing with version=${{ steps.tag_version.outputs.version }}" dotnet pack GFramework.sln \ -c Release \ --no-restore \ -o ./packages \ -p:PackageVersion=${{ steps.tag_version.outputs.version }} \ -p:IncludeSymbols=false - name: Validate packed modules run: | set -euo pipefail expected_packages=( "GeWuYou.GFramework" "GeWuYou.GFramework.Core" "GeWuYou.GFramework.Core.Abstractions" "GeWuYou.GFramework.Core.SourceGenerators" "GeWuYou.GFramework.Cqrs" "GeWuYou.GFramework.Cqrs.Abstractions" "GeWuYou.GFramework.Cqrs.SourceGenerators" "GeWuYou.GFramework.Ecs.Arch" "GeWuYou.GFramework.Ecs.Arch.Abstractions" "GeWuYou.GFramework.Game" "GeWuYou.GFramework.Game.Abstractions" "GeWuYou.GFramework.Game.SourceGenerators" "GeWuYou.GFramework.Godot" "GeWuYou.GFramework.Godot.SourceGenerators" ) mapfile -t actual_packages < <( find ./packages -maxdepth 1 -type f -name '*.nupkg' -printf '%f\n' \ | sed -E 's/\.[0-9][0-9A-Za-z.-]*\.nupkg$//' \ | sort -u ) printf '%s\n' "${expected_packages[@]}" | sort > expected-packages.txt printf '%s\n' "${actual_packages[@]}" | sort > actual-packages.txt echo "Expected packages:" cat expected-packages.txt echo "Actual packages:" cat actual-packages.txt diff -u expected-packages.txt actual-packages.txt - name: Show packages run: ls -la ./packages || true # 上传 nupkg 工件,供多个发布 job 复用,避免重复打包。 - name: Upload package artifacts uses: actions/upload-artifact@v7 with: name: packages path: ./packages/*.nupkg publish-nuget: name: Publish To NuGet.org runs-on: ubuntu-latest needs: build-pack permissions: contents: read packages: read id-token: write steps: - name: Setup .NET uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - name: Download package artifacts uses: actions/download-artifact@v8 with: name: packages path: ./packages - name: Show downloaded packages run: ls -la ./packages || true - name: NuGet login (OIDC → temporary API key) id: nuget_login uses: NuGet/login@v1 with: user: ${{ secrets.NUGET_USER }} # 将所有生成的包推送到 nuget.org。 # 使用临时 API 密钥进行身份验证,并跳过重复包上传。 - name: Push all packages to nuget.org env: NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} run: | set -e echo "Found API key: ${NUGET_API_KEY:+*** present ***}" pushed_any=false for PKG in ./packages/*.nupkg; do [ -f "$PKG" ] || continue pushed_any=true echo "Pushing $PKG to nuget.org..." dotnet nuget push "$PKG" \ --api-key "${NUGET_API_KEY}" \ --source https://api.nuget.org/v3/index.json \ --skip-duplicate done if [ "$pushed_any" = false ]; then echo "No packages found to push." fi publish-github-packages: name: Publish To GitHub Packages runs-on: ubuntu-latest needs: build-pack permissions: contents: read packages: write steps: - name: Setup .NET uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - name: Download package artifacts uses: actions/download-artifact@v8 with: name: packages path: ./packages - name: Show downloaded packages run: ls -la ./packages || true # 使用仓库内建的 GITHUB_TOKEN 配置 GitHub Packages NuGet 源。 - name: Configure GitHub Packages source run: | set -e dotnet nuget add source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ --name github \ --username "${{ github.repository_owner }}" \ --password "${{ github.token }}" \ --store-password-in-clear-text - name: Push all packages to GitHub Packages run: | set -e pushed_any=false for PKG in ./packages/*.nupkg; do [ -f "$PKG" ] || continue pushed_any=true echo "Pushing $PKG to GitHub Packages..." dotnet nuget push "$PKG" \ --source github \ --skip-duplicate done if [ "$pushed_any" = false ]; then echo "No packages found to push." fi create-release: name: Create GitHub Release runs-on: ubuntu-latest needs: - build-pack - publish-nuget - publish-github-packages if: ${{ always() && needs.build-pack.result == 'success' }} permissions: contents: write packages: read steps: - name: Checkout repository (at tag) uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: true - name: Download package artifacts uses: actions/download-artifact@v8 with: name: packages path: ./packages - name: Generate release notes id: cliff_release uses: orhun/git-cliff-action@v4 with: config: .github/cliff.toml args: >- -vv --latest --strip header env: OUTPUT: RELEASE_NOTES.md GITHUB_REPO: ${{ github.repository }} GITHUB_TOKEN: ${{ github.token }} # 无论某一侧包源发布是否失败,都继续创建 Release。 # 合规工件由独立 workflow 生成,当前发布流不再假设这些文件在同一次运行中可用。 - name: Create GitHub Release and Upload Assets uses: softprops/action-gh-release@v3 with: name: "Release ${{ github.ref_name }}" body_path: RELEASE_NOTES.md draft: false prerelease: false files: | ./packages/*.nupkg env: GITHUB_TOKEN: ${{ github.token }} - name: Write publish summary env: CLIFF_RELEASE_NOTES: ${{ steps.cliff_release.outputs.content }} run: | { echo "## GitHub Release" echo echo "- Tag: \`${{ github.ref_name }}\`" echo "- Package version: \`${{ needs.build-pack.outputs.package_version }}\`" echo printf '%s\n' "${CLIFF_RELEASE_NOTES}" } >> "${GITHUB_STEP_SUMMARY}"