用GitHub Actions实现代码度量和类图的自动化

112 阅读7分钟

嗨,朋友们,在我之前的文章--.NET GitHub Actions中介绍了GitHub Actions平台和工作流组成语法。你学到了如何将常见的.NET CLI命令和动作作为构建块,直接从你的GitHub仓库创建完全自动化的CI/CD管线。

这是专门介绍GitHub Actions平台和它对.NET开发者的意义的系列文章的第二篇。在这篇文章中,我将总结一个现有的用.NET编写的GitHub动作,它可以用来维护一个代码指标标记文件。然后我将解释如何用.NET创建自己的GitHub动作。我将向你展示如何定义元数据,用来识别GitHub repo作为一个动作。最后,我将用一个非常酷的例子把这一切结合起来:使用GitHub全新的Mermaid图表支持,更新.NET样本库中的一个动作,以包括对类图的支持,因此它将在每次提交时建立更新的类图。

用.NET编写一个自定义的GitHub Actions

Actions支持应用开发的几种变化。

Action 类型元数据指定器描述
Dockerruns.using: 'docker'任何可以作为Docker容器运行的应用程序。
脚本runs.using: 'javascript'任何Node.js应用程序(包括使用 actions/toolkit).
复合runs.using: 'composite'合成多个run 命令和uses 行动。

.NET能够在Docker容器中运行,当你想让一个功能齐全的.NET应用作为你的GitHub动作运行时--你就必须把你的应用容器化。关于.NET和Docker的更多信息,请看.NET文档。容器化的.NET应用

如果你对创建JavaScript的GitHub动作感到好奇,我也写过这方面的文章,见用机器翻译本地化.NET应用程序。在那篇博文中,我介绍了一个TypeScript动作,它依靠Azure认知服务来自动生成目标翻译的拉动请求。

除了对.NET应用进行容器化,你还可以创建一个.NET全局工具,可以使用run 语法来安装和调用,而不是uses 。这种替代方法对于创建一个可以作为全局工具使用的.NET CLI应用很有用,但它不在本篇文章的范围。关于.NET全局工具的更多信息,请参见.NET文档。全局工具

本教程的目的

在.NET文档中,有一个关于用.NET创建GitHub动作的教程。它涵盖了如何将一个.NET应用容器化,如何编写代表行动元数据的action.yml,以及如何从工作流中消费它。与其重复整个教程,我不如总结一下这个动作的意图,然后我们再看看它是如何被更新的。

教程中的应用通过以下方式进行代码度量分析:

  • 扫描并发现目标资源库中的**.csproj*.vbproj*项目文件。
  • 分析这些项目中发现的源代码,以获得。
    • 循环复杂性
    • 可维护性指数
    • 继承的深度
    • 类的耦合度
    • 源代码的行数
    • 可执行代码的近似行数
  • 创建(或更新)CODE_METRICS.md文件。

作为消耗工作流组成的一部分,当CODE_METRICS.md文件发生变化时,会有条件地(自动)创建一个拉动请求。换句话说,当你push 到你的GitHub仓库的变化时,工作流runsuses.NET代码度量行动--它更新代码度量的标记表示。CODE_METRICS.md文件本身是可以通过自动链接和可折叠的部分进行导航的。它使用表情符号来突出代码指标,例如,当一个类有很高的循环复杂性时,它会用一个表情符号来冒泡,直到项目层面的标记。在那里,你可以深入到类中,看到每个方法的度量。

Microsoft.CodeAnalysis.CodeMetrics 命名空间包含CodeAnalysisMetricData 类型,它暴露了CyclomaticComplexity 属性。这个属性是对代码的结构复杂性的测量。它是通过计算程序流程中不同代码路径的数量而产生的。一个具有复杂控制流的程序需要更多的测试来实现良好的代码覆盖率,而且可维护性较差。当代码被分析后,GitHub Action会更新CODE_METRICS.md文件,它使用以下代码在标题中写入一个表情符号。

internal static string ToCyclomaticComplexityEmoji(
    this CodeAnalysisMetricData metric) =>
    metric.CyclomaticComplexity switch
    {
        >= 0 and <= 7 => ":heavy_check_mark:",  // ✔
        8 or 9 => ":warning:",                  // ⚠
        10 or 11 => ":radioactive:",            // ☢
        >= 12 and <= 14 => ":x:",               // ❌
        _ => ":exploding_head:"                 // 🤯
    };

这个动作的所有代码都在.NET样本库中提供,也是.NET docs代码样本浏览器体验的一部分。作为一个使用实例,该动作是自我消耗的(或称狗粮)。随着代码的更新,该动作运行,并通过自动拉动请求维护CODE_METRICS.md文件。请看下面的屏幕截图,显示了markdown文件的一些主要部分。

CODE_METRICS.md标题

.NET code metrics markdown sample top-level heading.

CODE_METRICS.mdProjectFileReference drill-down heading

.NET code metrics markdown sample drill-down.

代码度量标记文件通过提供以下层次结构来表示它所分析的代码。

项目➡ 命名空间➡命名类型➡成员表

当你向下钻取一个命名类型时,在表的底部有一个链接,用于自动生成的类图。这将在添加新功能部分讨论。要自己浏览这个例子文件,请看.NET样本CODE_METRICS.md

行动元数据

要使一个GitHub仓库被识别为GitHub动作,它必须在action.yml文件中定义元数据:

# Name, description, and branding. All of which are used for
# displaying the action in the GitHub Action marketplace.
name: '.NET code metric analyzer'
description: 'A GitHub action that maintains a CODE_METRICS.md file,
              reporting cyclomatic complexity, maintainability index, etc.'
branding:
  icon: sliders
  color: purple

# Specify inputs, some are required and some are not.
inputs:
  owner:
    description: 'The owner of the repo. Assign from github.repository_owner. Example, "dotnet".'
    required: true
  name:
    description: 'The repository name. Example, "samples".'
    required: true
  branch:
    description: 'The branch name. Assign from github.ref. Example, "refs/heads/main".'
    required: true
  dir:
    description: 'The root directory to work from. Example, "path/to/code".'
    required: true
  workspace:
    description: 'The workspace directory.'
    required: false
    default: '/github/workspace'

# The action outputs the following values.
outputs:
  summary-title:
    description: 'The title of the code metrics action.'
  summary-details:
    description: 'A detailed summary of all the projects that were flagged.'
  updated-metrics:
    description: 'A boolean value, indicating whether or not the CODE_METRICS.md 
                  was updated as a result of running this action.'

# The action runs using docker and accepts the following arguments.
runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
  - '-o'
  - ${{ inputs.owner }}
  - '-n'
  - ${{ inputs.name }}
  - '-b'
  - ${{ inputs.branch }}
  - '-d'
  - ${{ inputs.dir }}
  - '-w'
  - ${{ inputs.workspace }}

.NET samples code metrics action的元数据被嵌套在一个子目录中,因此,它被识别为可以在GitHub Action市场中显示的动作。然而,它仍然可以作为一个动作使用。

关于元数据的更多信息,请参见GitHub 文档——GitHub 动作的元数据语法

消耗工作流程

要消费.NET代码度量操作,GitHub仓库根目录下的*.github/workflows*目录中必须存在一个工作流文件。请考虑以下工作流程文件。

name: '.NET code metrics'

on:
  push:
    branches: [ main ]
    paths-ignore:
    # Ignore CODE_METRICS.md and README.md files
    - '**.md'

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
        contents: write
        pull-requests: write

    steps:
    - uses: actions/checkout@v2

    # Analyze repositories source metrics:
    # Create (or update) CODE_METRICS.md file.
    - name: .NET code metrics
      id: dotnet-code-metrics
      uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
      with:
        owner: ${{ github.repository_owner }}
        name: ${{ github.repository }}
        branch: ${{ github.ref }}
        dir: ${{ './github-actions/DotNet.GitHubAction' }}

    # Create a pull request if there are changes.
    - name: Create pull request
      uses: peter-evans/create-pull-request@v3.4.1
      if: ${{ steps.dotnet-code-metrics.outputs.updated-metrics }} == 'true'
      with:
        title: '${{ steps.dotnet-code-metrics.outputs.summary-title }}'
        body: '${{ steps.dotnet-code-metrics.outputs.summary-details }}'
        commit-message: '.NET code metrics, automated pull request.'

这个工作流程利用了jobs.<job_id>.permissions ,将contentspull-requests 设置为write 。这是行动更新 repo 中的内容并从这些变化中创建拉动请求所需要的。关于权限的更多信息,请参见GitHub 文档。GitHub 动作的工作流语法 - 权限

为了帮助直观地了解这个工作流程的功能,请看下面的顺序图:

GitHub .NET code metrics workflow sequence diagram.

前面的顺序图显示了.NET代码度量行动的工作流程。

  1. 当开发者将代码推送到GitHub仓库的时候。
    1. 工作流程被触发并开始运行。
  2. 源代码被检出到$GITHUB_WORKSPACE
  3. .NET代码度量行动被调用。
    1. 源代码被分析,CODE_METRICS.md文件被更新。
  4. 如果.NET代码度量衡步骤(dotnet-code-metrics)输出度量衡被更新,create-pull-request 动作被调用。
    1. CODE_METRICS.md文件被检入版本库。
    2. 一个自动拉动请求被创建。关于由app/github-actions 机器人创建的拉动请求的例子,见.NET样本/拉动请求

添加新功能

GitHub最近宣布了对由Mermaid驱动的Markdown的图表支持。由于我们的自定义动作能够分析C#作为其执行的一部分,它对它所分析的类有一个语义上的理解。这被用来在CODE_METRICS.md文件中自动创建Mermaid类图。

.NET代码度量GitHub动作示例代码已经更新,以包括Mermaid支持:

static void AppendMermaidClassDiagrams(
    MarkdownDocument document,
    List<(string Id, string Class, string MermaidCode)> diagrams)
{
    document.AppendHeader("Mermaid class diagrams", 2);

    foreach (var (id, className, code) in diagrams)
    {
        document.AppendParagraph($"<div id="{id}"></div>");
        document.AppendHeader($"`{className}` class diagram", 5);
        document.AppendCode("mermaid", code);
    }
}

如果你有兴趣看到生成diagrams 参数的代码,请看 ToMermaidClassDiagram作为CODE_METRICS.mdMarkdown文件中的一个例子,请看下图:

.NET code metrics markdown class diagram.

总结

在这篇文章中,你了解了不同类型的GitHub动作,重点是Docker和.NET。我解释了如何更新.NET代码度量的GitHub Action,以包括对Mermaid类图的支持。你还看到了一个作为GitHub动作元数据的action.yml文件的例子。然后你看到了一个可视化的消费工作流程,以及如何消费代码度量的动作。我还介绍了如何为代码度量标准动作添加新的功能。

你将建立什么,以及它将如何帮助他人?我鼓励你创建并分享你自己的.NET GitHub动作。关于.NET和自定义GitHub动作的更多信息,请参阅以下资源: