基於DotNetty實現自動釋出 - 自動檢測程式碼變化

Broadm發表於2023-12-08

前言

很抱歉沒有實現上一篇的目標:一鍵釋出,因為工作量超出了預期,本次只實現了 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<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 修改記錄提取出待發布檔案

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

設定專案釋出路徑

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 傳送到伺服器

相關文章