教學課程:使用 .NET 建立 GitHub Action
瞭解如何建立可用來作為 GitHub Action 的 .NET 應用程式。 GitHub Actions 可啟用工作流程自動化與撰寫功能。 透過 GitHub Actions,您可以從 GitHub 建置、測試及部署原始程式碼。 此外,動作會公開以程式設計方式與問題互動、建立提取要求、執行程式碼檢閱及管理分支的能力。 如需持續與 GitHub Actions 整合的詳細資訊,請參閱 建置及測試 .NET 。
在本教學課程中,您會了解如何:
- 準備適用于 GitHub Actions 的 .NET 應用程式
- 定義動作輸入和輸出
- 撰寫工作流程
必要條件
- GitHub 帳戶
- .NET 6 SDK 或更新版本
- .NET 整合式開發環境 (IDE)
- 歡迎使用 Visual Studio IDE
應用程式的意圖
此教學課程中的應用程式會透過下列方式執行程式碼度量分析:
掃描及探索 *.csproj 與 *.vbproj 專案檔。
分析在這些專案內探索到的原始程式碼以了解:
- 循環複雜度
- 可維護性指數
- 繼承深度
- 類別結合程度
- 原始程式碼的行數
- 可執行檔程式碼的約略行數
建立 (或更新) CODE_METRICS.md 檔案。
應用程式不負責建立具有 CODE_METRICS.md 檔案變更的提取要求。 這些變更是當作工作流程撰寫的一部分來管理。
為簡潔起見,此教學課程中原始程式碼的參考省略了應用程式的數個部分。 完整的應用程式程式碼可在 GitHub 上取得。
探索應用程式
.NET 主控台應用程式會使用 CommandLineParser
NuGet 套件將引數剖析成 ActionInputs
物件。
using CommandLine;
namespace DotNet.GitHubAction;
public class ActionInputs
{
string _repositoryName = null!;
string _branchName = null!;
public ActionInputs()
{
if (Environment.GetEnvironmentVariable("GREETINGS") is { Length: > 0 } greetings)
{
Console.WriteLine(greetings);
}
}
[Option('o', "owner",
Required = true,
HelpText = "The owner, for example: \"dotnet\". Assign from `github.repository_owner`.")]
public string Owner { get; set; } = null!;
[Option('n', "name",
Required = true,
HelpText = "The repository name, for example: \"samples\". Assign from `github.repository`.")]
public string Name
{
get => _repositoryName;
set => ParseAndAssign(value, str => _repositoryName = str);
}
[Option('b', "branch",
Required = true,
HelpText = "The branch name, for example: \"refs/heads/main\". Assign from `github.ref`.")]
public string Branch
{
get => _branchName;
set => ParseAndAssign(value, str => _branchName = str);
}
[Option('d', "dir",
Required = true,
HelpText = "The root directory to start recursive searching from.")]
public string Directory { get; set; } = null!;
[Option('w', "workspace",
Required = true,
HelpText = "The workspace directory, or repository root directory.")]
public string WorkspaceDirectory { get; set; } = null!;
static void ParseAndAssign(string? value, Action<string> assign)
{
if (value is { Length: > 0 } && assign is not null)
{
assign(value.Split("/")[^1]);
}
}
}
上述動作輸入類別會定義數個必要的輸入,讓應用程式成功執行。 如果目前執行環境中提供 "GREETINGS"
環境變數值,建構函式將會寫入該環境變數值。 Name
與 Branch
屬性是從 "/"
分隔字串的最後一個區段剖析並指派的。
使用已定義的動作輸入類別,將焦點放在 Program.cs 檔案上。
using System.Text;
using CommandLine;
using DotNet.GitHubAction;
using DotNet.GitHubAction.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using static CommandLine.Parser;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddGitHubActionServices();
using IHost host = builder.Build();
ParserResult<ActionInputs> parser = Default.ParseArguments<ActionInputs>(() => new(), args);
parser.WithNotParsed(
errors =>
{
host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger("DotNet.GitHubAction.Program")
.LogError("{Errors}", string.Join(
Environment.NewLine, errors.Select(error => error.ToString())));
Environment.Exit(2);
});
await parser.WithParsedAsync(
async options => await StartAnalysisAsync(options, host));
await host.RunAsync();
static async ValueTask StartAnalysisAsync(ActionInputs inputs, IHost host)
{
// Omitted for brevity, here is the pseudo code:
// - Read projects
// - Calculate code metric analytics
// - Write the CODE_METRICS.md file
// - Set the outputs
var updatedMetrics = true;
var title = "Updated 2 projects";
var summary = "Calculated code metrics on two projects.";
// Do the work here...
// Write GitHub Action workflow outputs.
var gitHubOutputFile = Environment.GetEnvironmentVariable("GITHUB_OUTPUT");
if (!string.IsNullOrWhiteSpace(gitHubOutputFile))
{
using StreamWriter textWriter = new(gitHubOutputFile, true, Encoding.UTF8);
textWriter.WriteLine($"updated-metrics={updatedMetrics}");
textWriter.WriteLine($"summary-title={title}");
textWriter.WriteLine($"summary-details={summary}");
}
await ValueTask.CompletedTask;
Environment.Exit(0);
}
為簡潔起見,Program
檔案經過簡化,若要探索完整的範例來源,請參閱 Program.cs。 現有機制示範使用下列項目所需的未定案程式碼:
您可以使用外部專案或套件參考,並透過相依性插入註冊。 Get<TService>
是靜態區域函式,其需要 IHost
執行個體,並用來解析所需的服務。 透過 CommandLine.Parser.Default
單一資料庫,應用程式會從 args
取得 parser
執行個體。 當無法剖析引數時,應用程式會以非零的結束代碼結束。 如需詳細資訊,請參閱設定動作的結束代碼。
成功剖析引數時,會以必要的輸入正確呼叫應用程式。 在此情況下,會呼叫主要功能 StartAnalysisAsync
。
若要寫入輸出值,您必須遵循 GitHub Actions:設定輸出參數所辨識的格式。
準備適用于 GitHub Actions 的 .NET 應用程式
GitHub Actions 支援兩種應用程式開發變化
- JavaScript (選擇性 TypeScript)
- Docker 容器 (Docker 上執行的任何應用程式)
裝載 GitHub Action 的虛擬環境可能或可能尚未安裝 .NET。 如需目標環境中預先安裝項目的相關資訊,請參閱 GitHub Actions 虛擬環境。 雖然您可以從 GitHub Actions 工作流程執行 .NET CLI 命令,但為功能更完整的 。以 NET 為基礎的 GitHub Action,建議您容器化應用程式。 如需詳細資訊,請參閱 容器化 .NET 應用程式 。
Dockerfile
Dockerfile 是建置映像的一組指令。 對於 .NET 應用程式, Dockerfile 通常位於方案檔旁邊的目錄根目錄。
# Set the base image as the .NET 7.0 SDK (this includes the runtime)
FROM mcr.microsoft.com/dotnet/sdk:7.0@sha256:d32bd65cf5843f413e81f5d917057c82da99737cb1637e905a1a4bc2e7ec6c8d as build-env
# Copy everything and publish the release (publish implicitly restores and builds)
WORKDIR /app
COPY . ./
RUN dotnet publish ./DotNet.GitHubAction/DotNet.GitHubAction.csproj -c Release -o out --no-self-contained
# Label the container
LABEL maintainer="David Pine <david.pine@microsoft.com>"
LABEL repository="https://github.com/dotnet/samples"
LABEL homepage="https://github.com/dotnet/samples"
# Label as GitHub action
LABEL com.github.actions.name="The name of your GitHub Action"
# Limit to 160 characters
LABEL com.github.actions.description="The description of your GitHub Action."
# See branding:
# https://docs.github.com/actions/creating-actions/metadata-syntax-for-github-actions#branding
LABEL com.github.actions.icon="activity"
LABEL com.github.actions.color="orange"
# Relayer the .NET SDK, anew with the build output
FROM mcr.microsoft.com/dotnet/sdk:7.0@sha256:d32bd65cf5843f413e81f5d917057c82da99737cb1637e905a1a4bc2e7ec6c8d
COPY --from=build-env /app/out .
ENTRYPOINT [ "dotnet", "/DotNet.GitHubAction.dll" ]
注意
本教學課程中的 .NET 應用程式依賴 .NET SDK 作為其功能的一部分。 Dockerfile 會建立一組新的 Docker 圖層,與先前的圖層無關。 其會從頭開始使用 SDK 映像,並從先前的一組圖層新增組建輸出。 對於不需要 .NET SDK 作為其功能的一部分的應用程式 ,它們應該只依賴 .NET 執行時間。 這會大幅減少映像的大小。
FROM mcr.microsoft.com/dotnet/runtime:7.0
警告
請密切注意 Dockerfile 中的每個步驟,因為其與從「新增 Docker 支援」功能建立的標準 Dockerfile 不同。 特別是,最後幾個步驟會因未指定新的 WORKDIR
而有所不同,這會變更應用程式 ENTRYPOINT
的路徑。
上述 Dockerfile 步驟包括:
- 將
mcr.microsoft.com/dotnet/sdk:7.0
的基礎映像設定為別名build-env
。 - 複製內容併發布 .NET 應用程式:
- 應用程式是使用
dotnet publish
命令發佈的。
- 應用程式是使用
- 將標籤套用至容器。
- 轉寄 .NET SDK 映射的來源
mcr.microsoft.com/dotnet/sdk:7.0
- 從
build-env
複製已發佈的組建輸出。 - 定義委派給
dotnet /DotNet.GitHubAction.dll
的進入點。
提示
mcr.microsoft.com
中的 MCR 是 "Microsoft Container Registry" 的縮寫,而且是 Microsoft 官方 Docker 中樞同步發佈的容器目錄。 如需詳細資訊,請參閱 Microsoft 同步發佈容器目錄 (英文)。
警告
如果您使用 global.json 檔案來鎖定 SDK 版本,您應該明確地參考 Dockerfile 中的該版本。 例如,如果您已使用 global.json 來鎖定 SDK 版本 5.0.300
,您的 Dockerfile 應該使用 mcr.microsoft.com/dotnet/sdk:5.0.300
。 這可防止在發行新的次要修訂時中斷 GitHub Actions。
定義動作輸入和輸出
在探索應用程式一節中,您已了解 ActionInputs
類別。 此物件代表 GitHub Action 的輸入。 若要讓 GitHub 辨識存放庫是 GitHub Action,您的存放庫根目錄必須有 action.yml 檔案。
name: 'The title of your GitHub Action'
description: 'The description of your GitHub Action'
branding:
icon: activity
color: orange
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. Examples, "path/to/code".'
required: false
default: '/github/workspace'
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 action updated metrics.'
runs:
using: 'docker'
image: 'Dockerfile'
args:
- '-o'
- ${{ inputs.owner }}
- '-n'
- ${{ inputs.name }}
- '-b'
- ${{ inputs.branch }}
- '-d'
- ${{ inputs.dir }}
上述 action.yml 檔案會定義:
- GitHub Action 的
name
與description
branding
,其用於 GitHub Marketplace,有助於更唯一識別您的動作inputs
,其與ActionInputs
類別一對一對應outputs
,其會寫入Program
並當作工作流程撰寫的一部分使用runs
節點,可告知 GitHub 應用程式是docker
應用程式,以及要對其傳遞的引數
如需詳細資訊,請參閱 GitHub Actions 的中繼資料語法 (英文)。
預先定義的環境變數
使用 GitHub Actions,您預設會得到許多環境變數。 例如,變數 GITHUB_REF
將一律包含觸發工作流程執行之分支或標籤的參考。 GITHUB_REPOSITORY
具有擁有者與存放庫名稱,例如 dotnet/docs
。
您應該探索預先定義的環境變數,並據以使用這些環境變數。
工作流程撰寫
在 .NET 應用程式容器化 ,以及 已定義的動作輸入和輸出 之後,您就可以取用動作。 GitHub Actions 不需要在 GitHub Marketplace 中發佈,即可使用。 工作流程在存放庫的 .github/workflows 目錄中定義為 YAML 檔案。
# The name of the work flow. Badges will use this name
name: '.NET code metrics'
on:
push:
branches: [ main ]
paths:
- 'github-actions/DotNet.GitHubAction/**' # run on all changes to this dir
- '!github-actions/DotNet.GitHubAction/CODE_METRICS.md' # ignore this file
workflow_dispatch:
inputs:
reason:
description: 'The reason for running the workflow'
required: true
default: 'Manual run'
jobs:
analysis:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v3
- name: 'Print manual run reason'
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
echo 'Reason: ${{ github.event.inputs.reason }}'
- name: .NET code metrics
id: dotnet-code-metrics
uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
env:
GREETINGS: 'Hello, .NET developers!' # ${{ secrets.GITHUB_TOKEN }}
with:
owner: ${{ github.repository_owner }}
name: ${{ github.repository }}
branch: ${{ github.ref }}
dir: ${{ './github-actions/DotNet.GitHubAction' }}
- name: Create pull request
uses: peter-evans/create-pull-request@v4
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.'
重要
針對容器化的 GitHub Actions,您必須使用 runs-on: ubuntu-latest
。 如需詳細資訊,請參閱工作流程語法 jobs.<job_id>.runs-on
。
上述工作流程 YAML 檔案會定義三個主要節點:
- 工作流程的
name
。 此名稱也是建立工作流程狀態徽章時所使用的名稱。 on
節點會定義觸發動作的時機與方式。jobs
節點會概述每個作業內的各種作業與步驟。 個別步驟會取用 GitHub Actions。
如需詳細資訊,請參閱建立您的第一個工作流程。
將焦點放在 steps
節點上,撰寫更為明顯:
steps:
- uses: actions/checkout@v3
- name: 'Print manual run reason'
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
echo 'Reason: ${{ github.event.inputs.reason }}'
- name: .NET code metrics
id: dotnet-code-metrics
uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
env:
GREETINGS: 'Hello, .NET developers!' # ${{ secrets.GITHUB_TOKEN }}
with:
owner: ${{ github.repository_owner }}
name: ${{ github.repository }}
branch: ${{ github.ref }}
dir: ${{ './github-actions/DotNet.GitHubAction' }}
- name: Create pull request
uses: peter-evans/create-pull-request@v4
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.steps
代表工作流程撰寫。 步驟經過協調之後,使其成為循序、可溝通且可撰寫的步驟。 透過代表步驟的各種 GitHub Actions,使其每個步驟都有輸入和輸出,即可撰寫工作流程。
在上述步驟中,您可以觀察:
存放庫已簽出。
手動執行時,系統會將訊息列印至工作流程記錄檔。
識別為
dotnet-code-metrics
的步驟:uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
是本教學課程中容器化 .NET 應用程式的位置。env
會建立環境變數"GREETING"
,這是在應用程式執行時列印的。with
會指定每個必要的動作輸入。
當
dotnet-code-metrics
步驟指定值為true
的updated-metrics
輸出參數時,就會執行名為Create pull request
的條件式步驟。
重要
GitHub 允許建立加密的祕密。 使用 ${{ secrets.SECRET_NAME }}
語法即可在工作流程撰寫內使用祕密。 在 GitHub Action 的內容中,預設會自動填入 GitHub 權杖:${{ secrets.GITHUB_TOKEN }}
。 如需詳細資訊,請參閱 GitHub Actions 的內容和運算式語法 (英文)。
組合在一起
dotnet/samples GitHub 存放 庫是許多 .NET 範例原始程式碼專案的首頁,包括 本教學課程 中的應用程式。
產生的 CODE_METRICS.md 檔案是可瀏覽的。 此檔案代表其分析之專案的階層。 每個專案都有最上層區段,以及代表巢狀物件的最高循環複雜度整體狀態的 Emoji。 當您瀏覽檔案時,每個區段都會以每個區域的摘要,公開向下切入機會。 為方便起見,Markdown 包含可摺疊的區段。
階層可從:
- 專案檔至組件
- 組件至命名空間
- 命名空間至具名類型
- 每個具名類型都有一個資料表,而且每個資料表都有:
- 指向欄位、方法和屬性之行號的連結
- 程式碼度量的個別分級
作用中
工作流程會指定 on
一個 push
至 main
分支上,會觸發動作來執行。 執行時,GitHub 中的 [動作] 索引標籤會報告其執行的即時記錄資料流。 以下是 .NET code metrics
執行的範例記錄:
效能改善
如果您按照範例進行,可能會注意到每次使用此動作時,其都會針對該映像執行 docker build。 因此,每個觸發程序都需要一些時間來建置容器,然後再執行容器。 將 GitHub Actions 發行至 Marketplace 之前,您應該:
- (自動) 建置 Docker 映像
- 將 Docker 映像推送至 GitHub Container Registry (或其他任何公用容器登錄)
- 將動作變更為不建置映像,但使用公用登錄中的映像。
# Rest of action.yml content removed for readability
# using Dockerfile
runs:
using: 'docker'
image: 'Dockerfile' # Change this line
# using container image from public registry
runs:
using: 'docker'
image: 'docker://ghcr.io/some-user/some-registry' # Starting with docker:// is important!!
如需詳細資訊,請參閱 GitHub Docs:使用容器登錄 (英文)。
另請參閱
- .NET 泛型主機
- .NET 中的相依性插入
- 程式碼度量值
- 在 .NET 中建置開放原始碼的 GitHub Action 建置, 其中包含自動建置和推送 Docker 映射的工作流程 。