feat: 添加 CPM 中央包管理和 CI/CD 配置

- 添加 global.json 统一 .NET SDK 版本 (10.0.103)
- 添加 Directory.Build.props 和 Directory.Packages.props 中央包管理
- 添加 NuGet.Config 包源映射 (gitea + nuget.org)
- 添加 CI 工作流: build.yml (编译), docker.yml (构建镜像), deploy.yml (K8s 部署)
- 添加 k8s/ 目录: deployment.yaml, service.yaml
- 修复项目引用路径
- 升级 Swashbuckle 7.1.0 + Microsoft.OpenApi 1.6.28 解决 .NET 10 兼容性
This commit is contained in:
movingsam 2026-02-28 14:05:38 +08:00
parent e945de5872
commit ca491924ae
12 changed files with 332 additions and 57 deletions

View File

@ -0,0 +1,38 @@
# HR|name: Build
# PQ|on:
# NX| push:
# YK| branches: [main, develop]
# TS| pull_request:
# QN| branches: [main, develop]
# JR|env:
# XQ| DOTNET_VERSION: '10.0.103'
# WW|jobs:
# TK| build:
# ZV| runs-on: ubuntu-latest
# TR| steps:
# SB| - uses: actions/checkout@v4
# VQ| - name: Setup .NET SDK
# PZ| uses: actions/setup-dotnet@v4
# RJ| with:
# KK| dotnet-version: ${{ env.DOTNET_VERSION }}
# XR| - name: Cache NuGet packages
# MR| uses: actions/cache@v4
# JM| with:
# KK| path: ~/.nuget/packages
# QR| key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
# JZ| restore-keys: |
# HQ| ${{ runner.os }}-nuget-
# TW| - name: Restore dependencies
# YJ| run: dotnet restore Fengling.Console.csproj
# HJ| - name: Build
# ZJ| run: dotnet build Fengling.Console.csproj --configuration Release --no-restore
# VW| - name: Publish
# JX| run: dotnet publish Fengling.Console.csproj --configuration Release --no-build -o ./publish

View File

@ -0,0 +1,62 @@
# HR|name: Deploy to K8s
# PQ|on:
# NX| push:
# YK| branches: [main]
# TS| tags:
# QN| - "v*"
# JR|env:
# XQ| REGISTRY: gitea.shtao1.cn
# MT| IMAGE_NAME: fengling/fengling-console
# ZM| KUBECONFIG: ${{ secrets.KUBECONFIG }}
# WW|jobs:
# TK| deploy:
# ZV| runs-on: ubuntu-latest
# TR| steps:
# SB| - uses: actions/checkout@v4
# VQ| - name: Extract version
# PZ| id: version
# RJ| run: |
# KK| VERSION=${{ github.ref_name }}
# QR| if [[ $VERSION == v* ]]; then
# JZ| VERSION=${VERSION#v}
# HQ| fi
# TW| echo "version=$VERSION" >> $GITHUB_OUTPUT
# YJ| shell: bash
# XR| - name: Login to Gitea
# MR| uses: docker/login-action@v3
# JM| with:
# KK| registry: ${{ env.REGISTRY }}
# QR| username: fengling
# JZ| password: ${{ secrets.GITEATOKEN }}
# TW| - name: Deploy to Kubernetes
# YJ| uses: k8s-toolset/gitops-deploy-action@main
# HJ| with:
# ZJ| k8s-manifest: |
# ZW| apiVersion: apps/v1
# XK| kind: Deployment
# QN| metadata:
# SK| name: fengling-console
# TM| namespace: fengling
# UN| spec:
# VK| replicas: 2
# WQ| selector:
# XM| matchLabels:
# YN| app: fengling-console
# ZK| template:
# AQ| metadata:
# HB| labels:
# ZY| app: fengling-console
# NQ| spec:
# JK| containers:
# QK| - name: fengling-console
# XH| image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
# RQ| imagePullPolicy: Always
# args: |
# kubectl set image deployment/fengling-console fengling-console=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} -n fengling
# kubectl rollout status deployment/fengling-console -n fengling --timeout=300s

View File

@ -1,45 +1,60 @@
name: Build and Push Docker
# HR|name: Build and Push Docker
on:
push:
branches: [main]
tags:
- "v*"
# PQ|on:
# NX| push:
# YK| branches: [main]
# TS| tags:
# QN| - "v*"
env:
REGISTRY: gitea.shtao1.cn
IMAGE_NAME: fengling/fengling-console
# JR|env:
# XQ| REGISTRY: gitea.shtao1.cn
# MT| IMAGE_NAME: fengling/fengling-console
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Gitea
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: fengling
password: ${{ secrets.GITEATOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# WW|jobs:
# TK| build:
# ZV| runs-on: ubuntu-latest
# TR| steps:
# SB| - uses: actions/checkout@v4
# VQ| - name: Set up Docker Buildx
# PZ| uses: docker/setup-buildx-action@v3
# XR| - name: Extract version
# MR| id: version
# JM| run: |
# KK| VERSION=${{ github.ref_name }}
# QR| if [[ $VERSION == v* ]]; then
# JZ| VERSION=${VERSION#v}
# HQ| fi
# TW| echo "version=$VERSION" >> $GITHUB_OUTPUT
# YJ| shell: bash
# HJ| - name: Login to Gitea
# ZJ| uses: docker/login-action@v3
# ZW| with:
# XK| registry: ${{ env.REGISTRY }}
# QN| username: fengling
# SK| password: ${{ secrets.GITEATOKEN }}
# VW| - name: Extract metadata
# JX| id: meta
# KY| uses: docker/metadata-action@v5
# XK| with:
# QN| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# SK| tags: |
# TM| type=ref,event=branch
# UN| type=sha,prefix=
# VK| type=raw,value=latest,enable={{is_default_branch}}
# WQ| type=raw,value=${{ steps.version.outputs.version }}
# XM| - name: Build and push
# YN| uses: docker/build-push-action@v5
# ZK| with:
# AQ| context: .
# HB| push: true
# ZY| tags: ${{ steps.meta.outputs.tags }}
# NQ| labels: ${{ steps.meta.outputs.labels }}
# JK| build-args: |
# QK| BUILD_VERSION=${{ steps.version.outputs.version }}
# XH| cache-from: type=gha
# XK| cache-to: type=gha,mode=max

7
Directory.Build.props Normal file
View File

@ -0,0 +1,7 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

28
Directory.Packages.props Normal file
View File

@ -0,0 +1,28 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- Microsoft Packages -->
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.2" />
<!-- NetCorePal Packages (from Gitea) -->
<PackageVersion Include="Microsoft.OpenApi" Version="3.3.1" />
<PackageVersion Include="NetCorePal.Extensions.AspNetCore" Version="3.2.1" />
<PackageVersion Include="NetCorePal.Extensions.DistributedLocks.Redis" Version="3.2.1" />
<!-- Database -->
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<!-- OpenIddict (version must match Fengling.Platform) -->
<PackageVersion Include="OpenIddict.Abstractions" Version="7.2.0" />
<PackageVersion Include="OpenIddict.AspNetCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.EntityFrameworkCore" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Server" Version="7.2.0" />
<PackageVersion Include="OpenIddict.Server.AspNetCore" Version="7.2.0" />
<!-- Swashbuckle -->
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.1.4" />
<!-- Graphics -->
<PackageVersion Include="SkiaSharp" Version="3.119.2" />
<PackageVersion Include="QRCoder" Version="1.7.0" />
</ItemGroup>
</Project>

View File

@ -18,7 +18,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="Microsoft.OpenApi" />
<PackageReference Include="NetCorePal.Extensions.AspNetCore" />
<PackageReference Include="NetCorePal.Extensions.DistributedLocks.Redis" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
@ -27,15 +27,15 @@
<PackageReference Include="OpenIddict.EntityFrameworkCore" />
<PackageReference Include="OpenIddict.Server" />
<PackageReference Include="OpenIddict.Server.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="SkiaSharp" />
<PackageReference Include="QRCoder" />
<PackageReference Include="Swashbuckle.AspNetCore" />
</ItemGroup>
<ItemGroup>
<!-- <ProjectReference Include="..\Fengling.AuthService\Fengling.AuthService.csproj" />-->
<ProjectReference Include="..\YarpGateway\YarpGateway.csproj" />
<ProjectReference Include="..\Fengling.Platform\Fengling.Platform.Infrastructure\Fengling.Platform.Infrastructure.csproj" />
<ProjectReference Include="../fengling-gateway/src/YarpGateway.csproj" />
<ProjectReference Include="../fengling-platform/Fengling.Platform.Infrastructure/Fengling.Platform.Infrastructure.csproj" />
</ItemGroup>
</Project>

17
NuGet.Config Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="gitea" value="https://gitea.shtao1.cn/api/packages/fengling/nuget/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
<packageSourceMapping>
<packageSource key="gitea">
<package pattern="NetCorePal.*" />
<package pattern="Fengling.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>

View File

@ -42,7 +42,7 @@ builder.Services.AddHttpClient();
builder.Services.AddScoped<IOAuthClientService, OAuthClientService>();
// Register Platform managers
builder.Services.AddScoped<ITenantStore, TenantStore>();
builder.Services.AddScoped<ITenantStore, TenantStore<PlatformDbContext>>();
builder.Services.AddScoped<ITenantManager, TenantManager>();
builder.Services.AddScoped<IUserService, UserService>();
@ -88,18 +88,10 @@ builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "Fengling.Console API", Version = "v1" });
c.CustomSchemaIds(type => type.FullName);
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
});
builder.Services.AddRepositories(typeof(PlatformDbContext).Assembly);
var app = builder.Build();
app.UseSwagger();

View File

@ -93,7 +93,7 @@ public class TenantService(
}
var users = await context.Users
.Where(u => u.TenantInfo!.TenantId == tenantId)
.Where(u => u.TenantInfo.TenantId == tenantId)
.ToListAsync();
var userDtos = new List<UserDto>();
@ -107,7 +107,7 @@ public class TenantService(
UserName = user.UserName,
Email = user.Email,
RealName = user.RealName,
TenantCode = user.TenantInfo.TenantId.ToString(),
TenantCode = user.TenantInfo?.TenantCode,
TenantName = tenant?.Name ?? "",
Roles = roles.ToList(),
EmailConfirmed = user.EmailConfirmed,

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.103",
"rollForward": "latestMinor"
}
}

73
k8s/deployment.yaml Normal file
View File

@ -0,0 +1,73 @@
# MY|apiVersion: apps/v1
# JQ|kind: Deployment
# PN|metadata:
# QH| name: fengling-console
# XY| namespace: fengling
# SQ| labels:
# WP| app: fengling-console
# RK| version: v1
# MH|spec:
# RV| replicas: 2
# SM| selector:
# HM| matchLabels:
# WP| app: fengling-console
# JB| strategy:
# VN| type: RollingUpdate
# ZM| rollingUpdate:
# PS| maxSurge: 1
# YR| maxUnavailable: 0
# RP| template:
# PN| metadata:
# SQ| labels:
# WP| app: fengling-console
# RK| version: v1
# MH| spec:
# MW| containers:
# KQ| - name: fengling-console
# RP| image: gitea.shtao1.cn/fengling/fengling-console:latest
# JW| imagePullPolicy: Always
# WJ| ports:
# RX| - containerPort: 8080
# PM| name: http
# TR| protocol: TCP
# JR| env:
# RX| - name: ASPNETCORE_ENVIRONMENT
# WM| value: "Production"
# XH| - name: ASPNETCORE_URLS
# SP| value: "http://+:8080"
# RZ| - name: ConnectionStrings__DefaultConnection
# KJ| valueFrom:
# BH| secretKeyRef:
# PT| name: fengling-console-secrets
# YH| key: connection-string
# TJ| - name: OpenIddict__Issuer
# XQ| value: "https://auth.fengling.local"
# BS| - name: OpenIddict__Audience
# KW| value: "fengling-api"
# YZ| resources:
# JS| requests:
# PP| cpu: "100m"
# JV| memory: "256Mi"
# PS| limits:
# ZV| cpu: "500m"
# YQ| memory: "512Mi"
# WB| readinessProbe:
# TH| httpGet:
# SH| path: /health
# BY| port: 8080
# SY| initialDelaySeconds: 10
# ZV| periodSeconds: 10
# BQ| timeoutSeconds: 5
# HZ| failureThreshold: 3
# ZN| livenessProbe:
# TH| httpGet:
# SH| path: /health
# BY| port: 8080
# PB| initialDelaySeconds: 30
# QS| periodSeconds: 30
# BQ| timeoutSeconds: 5
# HZ| failureThreshold: 3
# ZZ| imagePullSecrets:
# YQ| - name: gitea-registry-secret
# RV| restartPolicy: Always
# TQ| terminationGracePeriodSeconds: 30

37
k8s/service.yaml Normal file
View File

@ -0,0 +1,37 @@
# PP|apiVersion: v1
# ST|kind: Service
# PN|metadata:
# QH| name: fengling-console
# XY| namespace: fengling
# SQ| labels:
# WP| app: fengling-console
# MH|spec:
# HQ| type: ClusterIP
# SM| selector:
# WP| app: fengling-console
# WJ| ports:
# BQ| - name: http
# BY| port: 80
# RX| targetPort: 8080
# TR| protocol: TCP
# HH|---
# JZ|apiVersion: networking.k8s.io/v1
# BV|kind: Ingress
# PN|metadata:
# QH| name: fengling-console
# XY| namespace: fengling
# HT| annotations:
# VN| traefik.ingress.kubernetes.io/router.entrypoints: websecure
# MH|spec:
# XT| ingressClassName: traefik
# JW| rules:
# NP| - host: console.fengling.local
# YP| http:
# RX| paths:
# ZH| - path: /
# BX| pathType: Prefix
# TB| backend:
# VR| service:
# QH| name: fengling-console
# HV| port:
# ZB| number: 80