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


    前言

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

    已完成的功能

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

    image
    image
    image

    简要说明

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

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

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

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

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

    部分代码

    发现解决方案

    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;
    }
    

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

    public static List GetChangesSinceLastPublish(string repoPath, string? lastCommitId = null)
    {
        var repo = GetRepo(repoPath);
    
        //获取上次发布的提交
        Commit? lastCommit = null;
        if (!string.IsNullOrEmpty(lastCommitId))
        {
            lastCommit = repo.Lookup(lastCommitId);
            if (lastCommit == null)
            {
                throw new Exception("无法获取上次发布的提交记录");
            }
        }
    
        //获取自上次提交以来的改动
        var diff = repo.Diff.Compare(lastCommit?.Tree, DiffTargets.Index);
        return [.. diff];
    }
    

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

    private List GetPublishFiles(IEnumerable<string> changedFilePaths)
    {
        var fileInfos = new List(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();
    }
    

    设置项目发布路径

    private async Task OkSetProjectReleaseDir()
    {
        if (string.IsNullOrEmpty(ReleaseDir) || !Directory.Exists(ReleaseDir))
        {
            Growl.ClearGlobal();
            Growl.ErrorGlobal("请正确设置项目发布路径");
            return;
        }
    
        var solutionRepo = Program.AppHost.Services.GetRequiredService();
        await solutionRepo.UpdateProjectReleaseDir(Id, ReleaseDir);
    
        setProjectReleaseDirDialog?.Close();
    
        Growl.SuccessGlobal("操作成功");
    }
    

    总结

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

    代码仓库

    项目暂且就叫 OpenDeploy

    欢迎大家拍砖,Star

    下一步

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

  • 相关阅读:
    3、微服务设计为什么要选择DDD
    基于FTP协议的文件上传与下载
    VS Code使用clang-format自定义C++代码默认格式化样式
    Fill(填充)和Polygon(覆铜)盖油问题
    Nexus 私服资源的上传下载
    01-Gin框架路由
    leetcode_力扣_1640. 能否连接形成数组
    Android车载蓝牙相关开发4:蓝牙电话操作器BluetoothHeadsetClient
    基于PHP+MySQL学生成绩管理系统的设计与实现
    2023P企业管理系统提供商,助力大中型企业一体化管理,免费更新
  • 原文地址:https://www.cnblogs.com/broadm/p/17884873.html