feat(ci): 添加发布工作流支持NuGet和GitHub Packages

- 实现自动构建和打包功能,支持标签触发
- 集成NuGet.org和GitHub Packages双重发布机制
- 添加许可证合规性检查和SBOM文件生成
- 实现GitHub Release自动创建和资产上传
- 配置OIDC身份验证和临时API密钥管理
- 添加包重复上传检测和跳过功能
This commit is contained in:
GeWuYou 2026-04-05 18:49:08 +08:00
parent a22e522cf9
commit 1f34928785

View File

@ -1,30 +1,33 @@
# 发布工作流NuGet + GitHub Release # 发布工作流NuGet + GitHub Packages + GitHub Release
# #
# 功能:当推送标签时自动构建、打包并发布到 NuGet.org 和 GitHub Release # 功能:当推送标签时自动构建、打包,并将相同产物并发发布到 NuGet.org 与 GitHub Packages
# 最后创建 GitHub Release。
# 触发条件:推送任何标签(如 v1.0.0 或 1.0.0 # 触发条件:推送任何标签(如 v1.0.0 或 1.0.0
# 权限:允许写入内容、包和使用 OIDC 身份验证 # 权限:允许写入内容、包和使用 OIDC 身份验证
name: Publish (NuGet + GitHub Release) name: Publish (NuGet + GitHub Packages + GitHub Release)
# 触发:推送 tag 时触发(例如 v1.0.0 或 1.0.0
on: on:
push: push:
tags: tags:
- '*' - '*'
# 顶级权限:允许创建 release、写 packages并允许 id-tokenOIDC
permissions: permissions:
contents: write contents: write
packages: write packages: write
id-token: write id-token: write
jobs: jobs:
build-and-publish: build-pack:
name: Build And Pack
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
contents: read
packages: read
id-token: write id-token: write
contents: write
packages: write outputs:
package_version: ${{ steps.tag_version.outputs.version }}
steps: steps:
- name: Checkout repository (at tag) - name: Checkout repository (at tag)
@ -38,19 +41,17 @@ jobs:
with: with:
dotnet-version: 10.0.x dotnet-version: 10.0.x
- name: Install unzip (for reading .nuspec from .nupkg)
run: sudo apt-get update && sudo apt-get install -y unzip
- name: Cache NuGet packages - name: Cache NuGet packages
uses: actions/cache@v5 uses: actions/cache@v5
with: with:
path: ~/.nuget/packages path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
# 从 GitHub 引用中提取标签版本 # 从 GitHub 引用中提取标签版本。
# 提取逻辑:去除 refs/tags/ 前缀,然后去除 v/V 前缀 # 提取逻辑:去除 refs/tags/ 前缀,然后去除 v/V 前缀。
# 输出version - 处理后的版本号
- name: Determine tag version - name: Determine tag version
id: tag_version id: tag_version
run: | run: |
@ -60,14 +61,25 @@ jobs:
VERSION=${TAG#v} VERSION=${TAG#v}
VERSION=${VERSION#V} VERSION=${VERSION#V}
echo "tag='$TAG' -> version='$VERSION'" echo "tag='$TAG' -> version='$VERSION'"
echo "version=$VERSION" >> $GITHUB_OUTPUT echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Pack (use tag version) - name: Pack (use tag version)
run: | run: |
set -e set -e
echo "Packing with version=${{ steps.tag_version.outputs.version }}" echo "Packing with version=${{ steps.tag_version.outputs.version }}"
dotnet pack -c Release -o ./packages -p:PackageVersion=${{ steps.tag_version.outputs.version }} -p:IncludeSymbols=false dotnet pack -c Release -o ./packages -p:PackageVersion=${{ steps.tag_version.outputs.version }} -p:IncludeSymbols=false
# 上传许可证合规相关的工件文件包括通知文件、第三方许可证、SBOM文件及验证结果
- 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
# 上传许可证合规相关的工件文件包括通知文件、第三方许可证、SBOM 文件及验证结果。
- name: Upload compliance artifacts - name: Upload compliance artifacts
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
@ -79,7 +91,25 @@ jobs:
sbom.cyclonedx.json sbom.cyclonedx.json
sbom-spdx-validation.txt sbom-spdx-validation.txt
sbom-cyclonedx-validation.txt sbom-cyclonedx-validation.txt
- name: Show packages
publish-nuget:
name: Publish To NuGet.org
runs-on: ubuntu-latest
needs: build-pack
permissions:
contents: read
packages: read
id-token: write
steps:
- name: Download package artifacts
uses: actions/download-artifact@v5
with:
name: packages
path: ./packages
- name: Show downloaded packages
run: ls -la ./packages || true run: ls -la ./packages || true
- name: NuGet login (OIDC → temporary API key) - name: NuGet login (OIDC → temporary API key)
@ -88,9 +118,8 @@ jobs:
with: with:
user: ${{ secrets.NUGET_USER }} user: ${{ secrets.NUGET_USER }}
# 将所有生成的包推送到 nuget.org # 将所有生成的包推送到 nuget.org。
# 使用临时 API 密钥进行身份验证 # 使用临时 API 密钥进行身份验证,并跳过重复包上传。
# 跳过重复包的上传
- name: Push all packages to nuget.org - name: Push all packages to nuget.org
env: env:
NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}
@ -110,36 +139,84 @@ jobs:
if [ "$pushed_any" = false ]; then if [ "$pushed_any" = false ]; then
echo "No packages found to push." echo "No packages found to push."
fi fi
# 从 .nupkg 文件中提取版本信息 publish-github-packages:
# 通过解压 .nupkgzip 格式)并读取 .nuspec 文件来获取版本 name: Publish To GitHub Packages
# 输出: runs-on: ubuntu-latest
# package_file - 第一个找到的包文件路径 needs: build-pack
# package_basename - 包文件的基本名称
# version - 从 nuspec 中解析出的版本号 permissions:
- name: Get Version and First Package Path contents: read
id: get_version 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@v5
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: | run: |
set -e set -e
PACKAGE_FILE=$(find ./packages -name "*.nupkg" | head -n 1 || true) dotnet nuget add source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \
if [ -z "$PACKAGE_FILE" ]; then --name github \
echo "No .nupkg file found in ./packages" --username "${{ github.repository_owner }}" \
exit 1 --password "${{ secrets.GITHUB_TOKEN }}" \
fi --store-password-in-clear-text
# 从 .nupkgzip里读取 .nuspec 并提取 <version>
VERSION=$(unzip -p "$PACKAGE_FILE" '*.nuspec' 2>/dev/null | sed -n 's:.*<version>\(.*\)</version>.*:\1:p' | head -n1)
if [ -z "$VERSION" ]; then
echo "Failed to parse version from $PACKAGE_FILE"
exit 1
fi
BASENAME=$(basename "$PACKAGE_FILE")
echo "package_file=$PACKAGE_FILE" >> $GITHUB_OUTPUT
echo "package_basename=$BASENAME" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
# 创建 GitHub Release - 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: Download package artifacts
uses: actions/download-artifact@v5
with:
name: packages
path: ./packages
- name: Download compliance artifacts
uses: actions/download-artifact@v5
with:
name: license-compliance
path: .
# 无论某一侧包源发布是否失败,都继续创建 Release并在正文中标注结果。
- name: Create GitHub Release and Upload Assets - name: Create GitHub Release and Upload Assets
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
with: with:
@ -147,7 +224,11 @@ jobs:
name: "Release ${{ github.ref_name }}" name: "Release ${{ github.ref_name }}"
body: | body: |
Release created by CI for tag ${{ github.ref_name }} Release created by CI for tag ${{ github.ref_name }}
Package version: ${{ steps.get_version.outputs.version }} Package version: ${{ needs.build-pack.outputs.package_version }}
## Publish Status
- NuGet.org publish: ${{ needs.publish-nuget.result }}
- GitHub Packages publish: ${{ needs.publish-github-packages.result }}
## Compliance ## Compliance
- NOTICE - NOTICE
@ -164,4 +245,4 @@ jobs:
sbom-spdx-validation.txt sbom-spdx-validation.txt
sbom-cyclonedx-validation.txt sbom-cyclonedx-validation.txt
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}