Kurz: Vytvoření akce GitHubu pomocí .NET

Zjistěte, jak vytvořit aplikaci .NET, která se dá použít jako akce GitHubu. GitHub Actions umožňuje automatizaci a složení pracovních postupů. Pomocí GitHub Actions můžete vytvářet, testovat a nasazovat zdrojový kód z GitHubu. Akce navíc zpřístupňují možnost programově pracovat s problémy, vytvářet žádosti o přijetí změn, provádět kontroly kódu a spravovat větve. Další informace o kontinuální integraci s GitHub Actions najdete v tématu Sestavování a testování .NET.

V tomto kurzu se naučíte:

  • Příprava aplikace .NET pro GitHub Actions
  • Definování vstupů a výstupů akcí
  • Vytvoření pracovního postupu

Požadavky

Záměr aplikace

Aplikace v tomto kurzu provádí analýzu metrik kódu pomocí:

  • Skenování a zjišťování souborů projektu *.csproj a *.vbproj .

  • Analýza zjištěného zdrojového kódu v rámci těchto projektů pro:

    • Cyklomaticová složitost
    • Index udržovatelnosti
    • Hloubka dědičnosti
    • Párování tříd
    • Počet řádků zdrojového kódu
    • Přibližné řádky spustitelného kódu
  • Vytvoření (nebo aktualizace) souboru CODE_METRICS.md

Aplikace neodpovídá za vytvoření žádosti o přijetí změn se změnami souboru CODE_METRICS.md. Tyto změny se spravují jako součást složení pracovního postupu.

Odkazy na zdrojový kód v tomto kurzu obsahují části aplikace, které jsou pro stručnost vynechány. Kompletní kód aplikace je k dispozici na GitHubu.

Prozkoumat aplikaci

Konzolová aplikace .NET používá CommandLineParser balíček NuGet k analýze argumentů do objektu 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]);
        }
    }
}

Předchozí třída vstupů akcí definuje několik požadovaných vstupů pro úspěšné spuštění aplikace. Konstruktor zapíše "GREETINGS" hodnotu proměnné prostředí, pokud je k dispozici v aktuálním spouštěcím prostředí. Branch Vlastnosti Name se analyzují a přiřazují z posledního "/" segmentu řetězce s oddělovači.

S definovanou třídou vstupů akcí se zaměřte na soubor 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);
}

Soubor Program je zjednodušený pro stručnost, abyste prozkoumali úplný ukázkový zdroj, viz Program.cs. Tato mechanika demonstruje často používaný kód potřebný k použití:

Odkazy na externí projekt nebo balíček lze použít a zaregistrovat pomocí injektáže závislostí. Jedná se Get<TService> o statickou místní funkci, která vyžaduje IHost instanci a slouží k vyřešení požadovaných služeb. CommandLine.Parser.Default S singletonem získá parser aplikace instanci z objektu args. Pokud argumenty nelze analyzovat, aplikace se ukončí nenulovým ukončovacím kódem. Další informace naleznete v tématu Nastavení ukončovací kódy pro akce.

Když se args úspěšně parsují, aplikace byla volána správně s požadovanými vstupy. V tomto případě se provede volání primární funkce StartAnalysisAsync .

Pokud chcete zapisovat výstupní hodnoty, musíte postupovat podle formátu, který GitHub Actions rozpozná: Nastavení výstupního parametru.

Příprava aplikace .NET pro GitHub Actions

GitHub Actions podporuje dvě varianty vývoje aplikací, a to buď

  • JavaScript (volitelně TypeScript)
  • Kontejner Dockeru (libovolná aplikace, která běží v Dockeru)

Virtuální prostředí, ve kterém je akce GitHubu hostovaná, může nebo nemusí mít nainstalovanou technologii .NET. Informace o tom, co je předinstalované v cílovém prostředí, najdete v tématu Virtuální prostředí GitHub Actions. I když je možné spouštět příkazy rozhraní příkazového řádku .NET z pracovních postupů GitHub Actions, aby bylo možné plně fungovat . Akci GitHubu založenou na technologii NET doporučujeme, abyste aplikaci kontejnerizovali. Další informace najdete v tématu Kontejnerizace aplikace .NET.

Soubor Dockerfile

Soubor Dockerfile je sada instrukcí pro sestavení image. V případě aplikací .NET se soubor Dockerfile obvykle nachází v kořenovém adresáři vedle souboru řešení.

# Set the base image as the .NET 7.0 SDK (this includes the runtime)
FROM mcr.microsoft.com/dotnet/sdk:7.0 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
COPY --from=build-env /app/out .
ENTRYPOINT [ "dotnet", "/DotNet.GitHubAction.dll" ]

Poznámka:

Aplikace .NET v tomto kurzu spoléhá na sadu .NET SDK jako součást jeho funkcí. Soubor Dockerfile vytvoří novou sadu vrstev Dockeru nezávisle na předchozích vrstvách. Začíná úplně od začátku s imagí sady SDK a přidá výstup sestavení z předchozí sady vrstev. U aplikací, které nevyžadují sadu .NET SDK jako součást jejich funkcí, by se místo toho měly spoléhat pouze na modul runtime .NET. Tím se výrazně zmenšuje velikost obrázku.

FROM mcr.microsoft.com/dotnet/runtime:7.0

Upozorňující

Věnujte pozornost každému kroku v souboru Dockerfile, protože se liší od standardního souboru Dockerfile vytvořeného z funkce "přidání podpory Dockeru". Konkrétně se poslední několik kroků liší tím, že nezadáte novou WORKDIR , která by změnila cestu k aplikaci ENTRYPOINT.

Mezi předchozí kroky souboru Dockerfile patří:

  • Nastavení základní image z mcr.microsoft.com/dotnet/sdk:7.0 aliasu build-env.
  • Kopírování obsahu a publikování aplikace .NET:
  • Použití popisků v kontejneru
  • Přenos image sady .NET SDK z mcr.microsoft.com/dotnet/sdk:7.0
  • Kopírování publikovaného výstupu sestavení z objektu build-env.
  • Definování vstupního bodu, na který delegáti .dotnet /DotNet.GitHubAction.dll

Tip

MCR mcr.microsoft.com je zkratka pro Microsoft Container Registry a je syndikovaný katalog kontejnerů Microsoftu z oficiálního centra Dockeru. Další informace naleznete v tématu Syndikáty katalogu kontejnerů Společnosti Microsoft.

Upozornění

Pokud k připnutí verze sady SDK použijete soubor global.json , měli byste explicitně odkazovat na tuto verzi v souboru Dockerfile. Pokud jste například ke připnutí verze sady SDK použili soubor global.json, měl by váš soubor Dockerfile použít mcr.microsoft.com/dotnet/sdk:5.0.300.5.0.300 Tím se zabrání porušení GitHub Actions při vydání nové dílčí revize.

Definování vstupů a výstupů akcí

V části Prozkoumat aplikaci jste se dozvěděli o ActionInputs třídě. Tento objekt představuje vstupy akce GitHubu. Aby GitHub rozpoznal, že úložiště je akce GitHubu, musíte mít soubor action.yml v kořenovém adresáři úložiště.

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

Předchozí soubor action.yml definuje:

  • Akce GitHubu name a description akce GitHubu
  • Nástroj branding, který se používá na GitHub Marketplace k jedinečnější identifikaci vaší akce
  • , inputskterý mapuje 1:1 s ActionInputs třídou
  • , outputskterý je zapsán do Program a používá se jako součást složení pracovního postupu
  • Uzel runs , který GitHubu říká, že aplikace je docker aplikace a jaké argumenty se mají předat do ní

Další informace najdete v tématu Syntaxe metadat pro GitHub Actions.

Předdefinované proměnné prostředí

Pomocí GitHub Actions získáte ve výchozím nastavení spoustu proměnných prostředí. Proměnná GITHUB_REF bude například vždy obsahovat odkaz na větev nebo značku, která aktivovala spuštění pracovního postupu. GITHUB_REPOSITORY má například název dotnet/docsvlastníka a úložiště .

Měli byste prozkoumat předem definované proměnné prostředí a odpovídajícím způsobem je používat.

Složení pracovního postupu

S kontejnerizovaným aplikací .NET a definovanými vstupy a výstupy akcí můžete akci využívat. Aby bylo možné gitHub Actions použít, není nutné publikovat na GitHub Marketplace. Pracovní postupy se definují v adresáři .github/workflows úložiště jako soubory 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.'

Důležité

Pro kontejnerizované GitHub Actions musíte použít runs-on: ubuntu-latest. Další informace naleznete v tématu Syntaxe jobs.<job_id>.runs-onpracovního postupu .

Předchozí soubor YAML pracovního postupu definuje tři primární uzly:

  • Pracovní name postup. Tento název se také používá při vytváření odznáček stavu pracovního postupu.
  • Uzel on definuje, kdy a jak se akce aktivuje.
  • Uzel jobs popisuje různé úlohy a kroky v rámci každé úlohy. Jednotlivé kroky využívají GitHub Actions.

Další informace naleznete v tématu Vytvoření prvního pracovního postupu.

Zaměření na steps uzel, složení je jasnější:

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.'

Představuje jobs.steps složení pracovního postupu. Kroky jsou orchestrovány tak, aby byly sekvenční, komunikační a kompozibilní. S různými akcemi GitHub Actions představujícími kroky se dají skládat pracovní postupy, které mají vstupy a výstupy.

V předchozích krocích můžete sledovat:

  1. Úložiště je rezervováno.

  2. Při ručním spuštění se do protokolu pracovního postupu vytiskne zpráva.

  3. Krok označený jako dotnet-code-metrics:

    • uses: dotnet/samples/github-actions/DotNet.GitHubAction@main je umístění kontejnerizované aplikace .NET v tomto kurzu.
    • env vytvoří proměnnou "GREETING"prostředí, která se vytiskne při provádění aplikace.
    • with určuje každý z požadovaných vstupů akce.
  4. Podmíněný krok s názvem Create pull request se spustí, když dotnet-code-metrics krok určuje výstupní parametr updated-metrics s hodnotou true.

Důležité

GitHub umožňuje vytvářet šifrované tajné kódy. Tajné kódy lze použít v rámci složení pracovního postupu pomocí ${{ secrets.SECRET_NAME }} syntaxe. V kontextu akce GitHubu existuje token GitHubu, který se ve výchozím nastavení automaticky vyplní: ${{ secrets.GITHUB_TOKEN }}. Další informace najdete v tématu Kontextová syntaxe a syntaxe výrazů pro GitHub Actions.

Spojení všech součástí dohromady

Úložiště dotnet/samples GitHubu je domovem mnoha projektů zdrojového kódu .NET, včetně aplikace v tomto kurzu.

Vygenerovaný soubor CODE_METRICS.md je možné procházet. Tento soubor představuje hierarchii projektů, které analyzoval. Každý projekt má oddíl nejvyšší úrovně a emoji, který představuje celkový stav nejvyšší složitosti cyklomatické pro vnořené objekty. Při procházení souboru každá část zveřejňuje příležitosti přechodu k podrobnostem se souhrnem jednotlivých oblastí. Markdown obsahuje sbalitelné oddíly jako další pohodlí.

Hierarchie postupuje od:

  • Soubor projektu do sestavení
  • Sestavení do oboru názvů
  • Obor názvů pro pojmenovaný typ
  • Každý pojmenovaný typ má tabulku a každá tabulka má:
    • Odkazy na čísla řádků pro pole, metody a vlastnosti
    • Individuální hodnocení metrik kódu

V praxi

Pracovní postup určuje, že onpushmain větev se aktivuje ke spuštění akce. Když se spustí, na kartě Akce na GitHubu se nahlásí stream živého protokolu spuštění. Tady je příklad protokolu ze .NET code metrics spuštění:

.NET code metrics - GitHub Actions log

Zlepšení výkonu

Pokud jste postupovali podle ukázky, možná jste si všimli, že při každém použití této akce provede sestavení Dockeru pro tuto image. Každá aktivační událost tedy čelí určité době sestavení kontejneru před jeho spuštěním. Než gitHub Actions vydáte na marketplace, měli byste:

  1. (automaticky) Sestavení image Dockeru
  2. Nasdílení image Dockeru do služby GitHub Container Registry (nebo jiného veřejného registru kontejneru)
  3. Změňte akci tak, aby se image nevytvořela, ale aby se používala image z veřejného registru.
# 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!!

Další informace najdete v dokumentaci GitHubu: Práce s registrem kontejneru.

Viz také

Další kroky