次の方法で共有


チュートリアル: .NET を使用して GitHub アクションを作成する

GitHub アクションとして使用できる .NET アプリを作成する方法について説明します。 GitHub Actions を 使用すると、ワークフローの自動化と構成が可能になります。 GitHub Actions を使用すると、GitHub からソース コードをビルド、テスト、デプロイできます。 さらに、アクションでは、プログラムによる問題の操作、プル要求の作成、コード レビューの実行、ブランチの管理を行う機能が公開されます。 GitHub Actions との継続的インテグレーションの詳細については、「 .NET のビルドとテスト」を参照してください。

このチュートリアルでは、以下の内容を学習します。

  • GitHub Actions 用の .NET アプリを準備する
  • アクションの入力と出力を定義する
  • ワークフローを作成する

[前提条件]

アプリの意図

このチュートリアルのアプリでは、次の方法でコード メトリック分析を実行します。

  • *.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 シングルトンを使用すると、アプリはparserからargs インスタンスを取得します。 引数を解析できない場合、アプリは 0 以外の終了コードで終了します。 詳細については、アクションの 終了コードの設定を参照してください。

引数が正常に解析されると、必要な入力でアプリが正しく呼び出されました。 この場合、主要な機能 StartAnalysisAsync の呼び出しが行われます。

出力値を書き込むには、 GitHub Actions: 出力パラメーターの設定で認識される形式に従う必要があります。

GitHub Actions 用の .NET アプリを準備する

GitHub Actions では、アプリ開発の 2 つのバリエーションがサポートされています。

  • JavaScript (必要に応じて TypeScript)
  • Docker コンテナー ( Docker で実行されるすべてのアプリ)

GitHub アクションがホストされている仮想環境には、.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

Warnung

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" を表し、公式の Docker ハブからの Microsoft のシンジケート コンテナー カタログです。 詳細については、「 Microsoft シンジケート コンテナー カタログ」を参照してください。

注意事項

global.json ファイルを使用して SDK のバージョンをピン留めする場合は、Dockerfile でそのバージョンを明示的に参照する必要があります。 たとえば、global.jsonを使用して SDK バージョン5.0.300をピン留めした場合、Dockerfile ではmcr.microsoft.com/dotnet/sdk:5.0.300を使用する必要があります。 これにより、新しいマイナー リビジョンがリリースされたときに GitHub Actions が中断されるのを防ぐことができます。

アクションの入力と出力を定義する

アプリの探索」 セクションで、 ActionInputs クラスについて学習しました。 このオブジェクトは、GitHub アクションの入力を表します。 GitHub がリポジトリが GitHub アクションであることを認識するには、リポジトリのルートに 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 アクションのnamedescription
  • brandingGitHub Marketplace で使用され、アクションをより一意に識別するのに役立ちます
  • inputsActionInputs クラスに1対1でマップされます。
  • outputsに書き込まれ、Programワークフローコンポジションの一部として使用される
  • runs ノード。アプリがdocker アプリケーションであり、それに渡す引数が GitHub に通知されます。

詳細については、「 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.'

Important

コンテナー化された GitHub Actions の場合は、 runs-on: ubuntu-latestを使用する必要があります。 詳細については、「 ワークフロー構文の jobs.<job_id>.runs-on」を参照してください。

上記のワークフロー YAML ファイルでは、次の 3 つのプライマリ ノードが定義されています。

  • ワークフローの 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 を使用して、それぞれ入力と出力を持つワークフローを構成できます。

前の手順では、次の内容を確認できます。

  1. リポジトリが チェックアウトされています

  2. 手動で実行すると、メッセージがワークフロー ログに出力されます。

  3. dotnet-code-metricsとして識別されるステップ:

    • uses: dotnet/samples/github-actions/DotNet.GitHubAction@main は、このチュートリアルのコンテナー化された .NET アプリの場所です。
    • env は、アプリの実行時に出力される環境変数 "GREETING"を作成します。
    • with は、必要な各アクション入力を指定します。
  4. Create pull requestという名前の条件付きステップは、dotnet-code-metrics ステップが値が updated-metricstrueの出力パラメーターを指定したときに実行されます。

Important

GitHub では、 暗号化されたシークレットを作成できます。 シークレットは、 ${{ secrets.SECRET_NAME }} 構文を使用して、ワークフローコンポジション内で使用できます。 GitHub アクションのコンテキストでは、既定で自動的に設定される GitHub トークンがあります: ${{ secrets.GITHUB_TOKEN }}。 詳細については、「 GitHub Actions のコンテキストと式の構文」を参照してください。

すべてをまとめる

dotnet/samples GitHub リポジトリには、このチュートリアルのアプリを含む多くの .NET サンプル ソース コード プロジェクトがあります。

生成された CODE_METRICS.md ファイルはナビゲート可能です。 このファイルは、分析したプロジェクトの階層を表します。 各プロジェクトには、最上位レベルのセクションと、入れ子になったオブジェクトのサイクロマティック複雑度が最も高い全体的な状態を表す絵文字があります。 ファイル内を移動すると、各セクションでドリルダウンの機会が公開され、各領域の概要が表示されます。 マークダウンには、便利な機能として折りたたみ可能なセクションがあります。

階層は次の項目から進行します。

  • アセンブリへのプロジェクト ファイル
  • 名前空間へのアセンブリ
  • 名前空間から名前付き型へ
  • 各名前付き型にはテーブルがあり、各テーブルには次の内容があります。
    • フィールド、メソッド、およびプロパティの行番号へのリンク
    • コード メトリックの個々の評価

動作中

ワークフローはonブランチにpushmainすることで、アクションが実行されるようにトリガーされると指定しています。 実行すると、GitHub の [アクション] タブに、その実行のライブ ログ ストリームが報告されます。 .NET code metrics実行のログの例を次に示します。

.NET コード メトリック - GitHub Actions ログ

パフォーマンスの向上

サンプルに従った場合、このアクションが使用されるたびに、そのイメージの Docker ビルド が実行されることに気付いたかもしれません。 そのため、すべてのトリガーは、コンテナーを実行する前にコンテナーをビルドする時間が必要です。 GitHub Actions をマーケットプレースにリリースする前に、次の作業を行う必要があります。

  1. (自動的に)Docker イメージをビルドする
  2. Docker イメージを GitHub Container Registry (またはその他のパブリック コンテナー レジストリ) にプッシュする
  3. イメージをビルドするのではなく、パブリック レジストリのイメージを使用するようにアクションを変更します。
# 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: コンテナー レジストリの操作」を参照してください。

こちらも参照ください

次のステップ