环境:net6.0
包:Nuke.Common
dotnet add package Nuke.Common --version 8.1.4
1、安装 Nuke.GlobalTool
dotnet tool install Nuke.GlobalTool --global
2、项目目录创建文件夹 .nuke

3、 .nuke文件夹下创建文件 build.schema.json ,内容如下
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Build Schema",
"properties": {
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
}
}
}
4、Build.cs 代码
cs
/// <summary>
/// cmd指令
/// dotnet run --project ProjectBuild.csproj -- CopyToTarget # 完整重新构建打包
/// dotnet run --project ProjectBuild.csproj -- Compile # 只编译
/// dotnet run --project ProjectBuild.csproj -- Publish # 只发布
/// dotnet run --project ProjectBuild.csproj -- CopyOnly # 只复制
/// </summary>
class Build : NukeBuild
{
public static int Main() => Execute<Build>(x => x.CopyToTarget);
[Parameter("Configuration to build - Default is 'Release'")]
readonly string Configuration = "Release";
/// <summary>
/// 复制目标目录
/// </summary>
[Parameter("Target directory for copying files")]
readonly string TargetDirectory = "C:\\Program Files\\PEER Group\\PTO 8.6 SP2\\PtoUpgrade";
// 定义需要编译和发布的项目
Dictionary<string, string> Projects => new Dictionary<string, string>
{
{ "D:\\项目\\mc-api\\src\\AM.MC.WebApi\\AM.MC.WebApi.csproj", "mc-api" },
{ "D:\\项目\\mcm\\src\\AM.Mcm.WebApi\\AM.Mcm.WebApi.csproj", "mcm-api" },
{ "D:\\项目\\webui\\AM.MC.WebBlazorServer\\AM.MC.WebBlazorServer.csproj", "pe-web" }
};
// 排除文件规则(支持通配符)
List<string> ExcludePatterns => new List<string>
{
// 排除文件
"*.pdb", // 调试符号文件
"appsettings.Development.json",
"appsettings.json",
// 排除文件夹
"logs/", // 日志文件夹
"temp/", // 临时文件夹
"RedisServer/",
// 排除特定文件
"*.log",
"*.tmp",
".gitignore",
".gitattributes"
};
// 输出目录
AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
AbsolutePath PublishDirectory => ArtifactsDirectory / "publish";
AbsolutePath CopyTargetDirectory => (AbsolutePath)TargetDirectory;
Target CheckPaths => _ => _
.Executes(() =>
{
Log.Information("验证项目文件路径...");
var missingProjects = new List<string>();
foreach (var project in Projects)
{
if (!File.Exists(project.Key))
{
Log.Error($"✗ 找不到项目: {project.Key}");
missingProjects.Add(project.Key);
}
else
{
Log.Information($"✓ 找到: {project.Key}");
}
}
if (missingProjects.Any())
{
throw new FileNotFoundException(
$"找不到 {missingProjects.Count} 个项目文件,请检查路径配置");
}
Log.Information($"发布目录: {PublishDirectory}");
Log.Information($"目标目录: {CopyTargetDirectory}");
Log.Information($"共找到 {Projects.Count} 个项目");
Log.Information($"排除规则数: {ExcludePatterns.Count}");
});
Target Clean => _ => _
.DependsOn(CheckPaths)
.Before(Restore)
.Executes(() =>
{
Log.Information("清理输出目录...");
EnsureCleanDirectory(ArtifactsDirectory);
EnsureCleanDirectory(PublishDirectory);
Log.Information("清理完成");
});
Target Restore => _ => _
.DependsOn(Clean)
.Executes(() =>
{
Log.Information("开始恢复 NuGet 包...");
int count = 0;
foreach (var project in Projects)
{
count++;
Log.Information($"[{count}/{Projects.Count}] 恢复: {project.Value}");
DotNetRestore(s => s.SetProjectFile(project.Key));
}
Log.Information("所有包恢复完成");
});
Target Compile => _ => _
.DependsOn(Restore)
.Executes(() =>
{
Log.Information("开始编译项目...");
int count = 0;
foreach (var project in Projects)
{
count++;
Log.Information($"[{count}/{Projects.Count}] 编译: {project.Value} [{Configuration}]");
DotNetBuild(s => s
.SetProjectFile(project.Key)
.SetConfiguration(Configuration)
.EnableNoRestore());
}
Log.Information("所有项目编译完成");
});
Target Publish => _ => _
.DependsOn(Compile)
.Executes(() =>
{
Log.Information("开始发布项目...");
int count = 0;
foreach (var project in Projects)
{
count++;
var publishPath = PublishDirectory / project.Value;
Log.Information($"[{count}/{Projects.Count}] 发布: {project.Value} -> {publishPath}");
DotNetPublish(s => s
.SetProject(project.Key)
.SetConfiguration(Configuration)
.SetOutput(publishPath)
.EnableNoRestore()
.DisableSelfContained()
);
}
Log.Information("所有项目发布完成");
});
Target CopyToTarget => _ => _
.DependsOn(Publish)
.Executes(() =>
{
Log.Information("开始复制文件到目标目录...");
// 确保目标目录存在
EnsureExistingDirectory(CopyTargetDirectory);
var publishedProjects = Directory.GetDirectories(PublishDirectory);
Log.Information($"找到 {publishedProjects.Length} 个发布目录");
Log.Information($"目标根目录: {CopyTargetDirectory}");
Log.Information("");
int projectCount = 0;
int totalCopiedFiles = 0;
int totalExcludedFiles = 0;
foreach (var projectDir in publishedProjects)
{
projectCount++;
var projectName = Path.GetFileName(projectDir);
var targetProjectDir = CopyTargetDirectory / projectName;
Log.Information($"[{projectCount}/{publishedProjects.Length}] 处理项目: {projectName}");
Log.Information($" 源目录: {projectDir}");
Log.Information($" 目标目录: {targetProjectDir}");
// 清理目标目录(如果存在)
if (Directory.Exists(targetProjectDir))
{
Log.Information($" 清理已存在的目录...");
DeleteDirectory(targetProjectDir);
}
// 复制文件(应用排除规则)
var (copiedCount, excludedCount) = CopyDirectoryWithExclusions(
projectDir,
targetProjectDir,
ExcludePatterns);
totalCopiedFiles += copiedCount;
totalExcludedFiles += excludedCount;
Log.Information($" ✓ 已复制 {copiedCount} 个文件,排除 {excludedCount} 个文件");
Log.Information("");
}
Log.Information("========================================");
Log.Information(" 复制完成统计");
Log.Information("========================================");
Log.Information($" 处理项目数: {projectCount}");
Log.Information($" 复制文件数: {totalCopiedFiles}");
Log.Information($" 排除文件数: {totalExcludedFiles}");
Log.Information($" 目标目录: {CopyTargetDirectory}");
Log.Information("========================================");
});
// 复制目录并应用排除规则
private (int copiedCount, int excludedCount) CopyDirectoryWithExclusions(
string sourceDir,
string targetDir,
List<string> excludePatterns)
{
int copiedCount = 0;
int excludedCount = 0;
// 创建目标目录
Directory.CreateDirectory(targetDir);
// 获取所有文件
var allFiles = Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories);
foreach (var sourceFile in allFiles)
{
// 获取相对路径
var relativePath = Path.GetRelativePath(sourceDir, sourceFile);
// 检查是否应该排除
if (ShouldExclude(relativePath, excludePatterns))
{
excludedCount++;
continue;
}
// 构建目标文件路径
var targetFile = Path.Combine(targetDir, relativePath);
var targetFileDir = Path.GetDirectoryName(targetFile);
// 确保目标目录存在
if (!Directory.Exists(targetFileDir))
{
Directory.CreateDirectory(targetFileDir);
}
// 复制文件
File.Copy(sourceFile, targetFile, overwrite: true);
copiedCount++;
}
return (copiedCount, excludedCount);
}
// 检查文件或路径是否应该被排除
private bool ShouldExclude(string relativePath, List<string> excludePatterns)
{
// 标准化路径分隔符
relativePath = relativePath.Replace('\\', '/');
foreach (var pattern in excludePatterns)
{
var normalizedPattern = pattern.Replace('\\', '/');
// 处理文件夹排除(以 / 结尾)
if (normalizedPattern.EndsWith("/"))
{
var folderPattern = normalizedPattern.TrimEnd('/');
if (relativePath.StartsWith(folderPattern + "/", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
// 处理通配符匹配
else if (normalizedPattern.Contains("*") || normalizedPattern.Contains("?"))
{
var regexPattern = "^" + Regex.Escape(normalizedPattern)
.Replace("\\*", ".*")
.Replace("\\?", ".") + "$";
var fileName = Path.GetFileName(relativePath);
if (Regex.IsMatch(fileName, regexPattern, RegexOptions.IgnoreCase))
{
return true;
}
}
// 精确匹配
else
{
var fileName = Path.GetFileName(relativePath);
if (fileName.Equals(normalizedPattern, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
// 额外目标:只复制(不重新编译)
Target CopyOnly => _ => _
.Executes(() =>
{
Log.Information("只复制已发布的文件(跳过编译)...");
if (!Directory.Exists(PublishDirectory))
{
throw new DirectoryNotFoundException(
$"发布目录不存在: {PublishDirectory}。请先运行 Publish 目标。");
}
EnsureExistingDirectory(CopyTargetDirectory);
var publishedProjects = Directory.GetDirectories(PublishDirectory);
if (publishedProjects.Length == 0)
{
throw new InvalidOperationException(
$"发布目录中没有找到项目。请先运行 Publish 目标。");
}
Log.Information($"找到 {publishedProjects.Length} 个发布目录");
int totalCopiedFiles = 0;
int totalExcludedFiles = 0;
foreach (var projectDir in publishedProjects)
{
var projectName = Path.GetFileName(projectDir);
var targetProjectDir = CopyTargetDirectory / projectName;
Log.Information($"复制: {projectName}");
if (Directory.Exists(targetProjectDir))
{
DeleteDirectory(targetProjectDir);
}
var (copiedCount, excludedCount) = CopyDirectoryWithExclusions(
projectDir,
targetProjectDir,
ExcludePatterns);
totalCopiedFiles += copiedCount;
totalExcludedFiles += excludedCount;
Log.Information($" ✓ 已复制 {copiedCount} 个文件,排除 {excludedCount} 个文件");
}
Log.Information($"✓ 复制完成:{totalCopiedFiles} 个文件已复制到 {CopyTargetDirectory}");
});
// 显示排除规则
Target ShowExcludeRules => _ => _
.Executes(() =>
{
Log.Information("========================================");
Log.Information(" 当前排除规则");
Log.Information("========================================");
var filePatterns = ExcludePatterns.Where(p => !p.EndsWith("/")).ToList();
var folderPatterns = ExcludePatterns.Where(p => p.EndsWith("/")).ToList();
Log.Information("");
Log.Information("排除的文件模式:");
foreach (var pattern in filePatterns)
{
Log.Information($" - {pattern}");
}
Log.Information("");
Log.Information("排除的文件夹:");
foreach (var pattern in folderPatterns)
{
Log.Information($" - {pattern.TrimEnd('/')}");
}
Log.Information("");
Log.Information($"共 {ExcludePatterns.Count} 条排除规则");
Log.Information("========================================");
});
}
指定csproj目录:
cs
Dictionary<string, string> Projects => new Dictionary<string, string>
{
{ "D:\\项目\\mc-api\\src\\AM.MC.WebApi\\AM.MC.WebApi.csproj", "mc-api" },
{ "D:\\项目\\mcm\\src\\AM.Mcm.WebApi\\AM.Mcm.WebApi.csproj", "mcm-api" },
{ "D:\\项目\\webui\\AM.MC.WebBlazorServer\\AM.MC.WebBlazorServer.csproj", "pe-web" }
};