基于DotNetty实现自动发布 - 自动检测代码变化

前言

很抱歉没有实现上一篇的目标:一键发布,因为工作量超出了预期,本次只实现了 Git 代码变化检测

已完成的功能

  • 解决方案的项目发现与配置
  • 首次发布需要手动处理
  • 自动检测代码变化并解析出待发布的文件



简要说明

  • 只需要填写解决方案的 Git 仓储路径即可自动发现项目 (通过查找 .csproj 文件实现)

  • 自动发现 Web 项目 (通过判断项目根目录是否包含 Web.config 实现) PS: 只支持 .NET Framework

  • 需要配置 Web 项目的发布目录, 编译还需要手动执行

  • 首次发布需要手动执行, 然后保存此次发布对应的 Git 提交 ID

  • 后续发布,可以根据上次发布记录,自动解析出待待发布的文件

部分代码

发现解决方案

C# 复制代码
private static Solution DetectSolution(string gitRepoPath)
{
    string[] solutionFilePaths = Directory.GetFiles(gitRepoPath, "*.sln", SearchOption.AllDirectories);
    if (solutionFilePaths == null || solutionFilePaths.Length == 0)
    {
        throw new Exception("未找到解决方案");
    }
    string[] projectFilePaths = Directory.GetFiles(gitRepoPath, "*.csproj", SearchOption.AllDirectories);
    if (projectFilePaths == null || projectFilePaths.Length == 0)
    {
        throw new Exception("未找到项目");
    }

    var solutionFilePath = solutionFilePaths[0];
    var solutionDir = Path.GetDirectoryName(solutionFilePath);
    var solutionName = Path.GetFileNameWithoutExtension(solutionFilePath);

    var solution = new Solution
    {
        GitRepositoryPath = gitRepoPath,
        SolutionDir = solutionDir!,
        SolutionName = solutionName
    };

    foreach (var projectFilePath in projectFilePaths)
    {
        var projectDir = Path.GetDirectoryName(projectFilePath);
        var projectName = Path.GetFileNameWithoutExtension(projectFilePath);
        var webConfigFiles = Directory.GetFiles(projectDir!, "web.config", SearchOption.TopDirectoryOnly);
        var project = new Project
        {
            ProjectDir = projectDir!,
            ProjectName = projectName,
            IsWeb = webConfigFiles != null && webConfigFiles.Length > 0,
            SolutionName = solutionName,
            ReleaseDir = string.Empty
        };
        solution.Projects.Add(project);
    }
    return solution;
}

获取自上次发布以来的改动

C# 复制代码
public static List<PatchEntryChanges> GetChangesSinceLastPublish(string repoPath, string? lastCommitId = null)
{
    var repo = GetRepo(repoPath);

    //获取上次发布的提交
    Commit? lastCommit = null;
    if (!string.IsNullOrEmpty(lastCommitId))
    {
        lastCommit = repo.Lookup<Commit>(lastCommitId);
        if (lastCommit == null)
        {
            throw new Exception("无法获取上次发布的提交记录");
        }
    }

    //获取自上次提交以来的改动
    var diff = repo.Diff.Compare<Patch>(lastCommit?.Tree, DiffTargets.Index);
    return [.. diff];
}

从 Git 修改记录提取出待发布文件

C# 复制代码
private List<DeployFileInfo> GetPublishFiles(IEnumerable<string> changedFilePaths)
{
    var fileInfos = new List<DeployFileInfo>(changedFilePaths.Count());
    foreach (string changedPath in changedFilePaths)
    {
        var fi = DeployFileInfo.Create(changedPath);
        if (fi.IsUnKnown) continue;
        fileInfos.Add(fi);
    }
    foreach (var fi in fileInfos)
    {
        fi.ChangedFileAbsolutePath = Path.Combine(GitRepositoryPath, fi.ChangedFileRelativePath);

        //所属项目
        var project = Projects
            .Where(a => fi.ChangedFileRelativePath.Contains(a.ProjectName, StringComparison.OrdinalIgnoreCase))
            .FirstOrDefault();
        if (project == null) continue;

        fi.ProjectName = project.ProjectName;
        if (fi.IsDLL)
        {
            fi.FileName = $"{project.ProjectName}.dll";
            fi.PublishFileRelativePath = $"bin\\{fi.FileName}";
        }
        else
        {
            fi.PublishFileRelativePath = fi.ChangedFileAbsolutePath.Replace(project.ProjectDir, "").TrimStart(Path.DirectorySeparatorChar);
        }
        fi.PublishFileAbsolutePath = Path.Combine(webProject!.ReleaseDir, fi.PublishFileRelativePath);

        //Logger.Info(fi.ToJsonString(true));
    }
    //按照 PublishFileAbsolutePath 去重
    return fileInfos.Distinct(new DeployFileInfoComparer()).ToList();
}

设置项目发布路径

C# 复制代码
private async Task OkSetProjectReleaseDir()
{
    if (string.IsNullOrEmpty(ReleaseDir) || !Directory.Exists(ReleaseDir))
    {
        Growl.ClearGlobal();
        Growl.ErrorGlobal("请正确设置项目发布路径");
        return;
    }

    var solutionRepo = Program.AppHost.Services.GetRequiredService<SolutionRepository>();
    await solutionRepo.UpdateProjectReleaseDir(Id, ReleaseDir);

    setProjectReleaseDirDialog?.Close();

    Growl.SuccessGlobal("操作成功");
}

总结

本篇主要实现了 Git 代码变化的自动检测

代码仓库

项目暂且就叫 OpenDeploy 吧

欢迎大家拍砖,Star

下一步

计划下一步,实现一键发布,把待发布的文件一次性打包通过 DotNetty 发送到服务器

相关推荐
huaqianzkh1 天前
WinForm C#:循环修改控件UI闪烁卡顿?SuspendLayout / ResumeLayout 与 BeginUpdate 优化
开发语言·ui·c#
用户3721574261352 天前
C# 实现 Excel 单元格保护:锁定公式并允许编辑指定区域
c#·ai编程·编程语言
公子小六2 天前
基于.NET的Windows窗体编程之WinForms数据持久化(1)
windows·microsoft·c#·.net·winforms
三雷科技2 天前
第6章第4节:自定义分词器
开发语言·c#
zhanghaha13142 天前
C# 系列教程_12 字符串 string 保姆级系统教程(面向新手,大量示例 + 原理讲解)
开发语言·数据库·c#
Coder_Shenshen3 天前
罗克韦尔ACD工程归档离线变量树解析
算法·c#
samble3 天前
从零搭建半导体设备上位机(九):拖拽与交互,Adorner、DragDrop 与晶圆拖放信息
wpf·拖拽·adorner
寂寞的船3 天前
【Unity知识速递】第10期 Unity GC 零分配实战:从 UnityEngine.Pool 到手动 GC 控制的全链路优化
jvm·unity·c#·游戏引擎·lucene
1-1=03 天前
C# 数据结构和特殊类型
数据结构·c#·.net·.netcore
郝学胜-神的一滴4 天前
AI 编程智能体 27:吃透MCP通信机制
人工智能·python·langchain·wpf·numpy·scikit-learn