Oktatóanyag: GitHub-művelet létrehozása .NET-tel

Megtudhatja, hogyan hozhat létre egy GitHub-műveletként használható .NET-alkalmazást. A GitHub Actions lehetővé teszi a munkafolyamat automatizálását és összetételét. A GitHub Actions segítségével forráskódot hozhat létre, tesztelhet és helyezhet üzembe a GitHubról. Emellett a műveletek programozott módon kezelhetik a problémákat, lekéréses kérelmeket hozhatnak létre, kód-felülvizsgálatokat hajthatnak végre, és kezelhetik az ágakat. A GitHub Actions szolgáltatással való folyamatos integrációról további információt a .NET létrehozása és tesztelése című témakörben talál.

Ebben az oktatóanyagban az alábbiakkal fog megismerkedni:

  • .NET-alkalmazás előkészítése a GitHub Actionshez
  • Műveleti bemenetek és kimenetek definiálása
  • Munkafolyamat összeállítása

Előfeltételek

Az alkalmazás szándéka

Az oktatóanyagban szereplő alkalmazás a következőkkel végzi el a kódmetrika elemzését:

  • *.csproj és *.vbproj projektfájlok vizsgálata és felderítése.

  • A felderített forráskód elemzése a következő projektekben:

    • Ciklonmatikus összetettség
    • Karbantarthatósági index
    • Az öröklés mélysége
    • Osztálykapcsoló
    • Forráskódsorok száma
    • Végrehajtható kód hozzávetőleges sora
  • CODE_METRICS.md fájl létrehozása (vagy frissítése).

Az alkalmazás nem felelős azért, hogy lekéréses kérelmet hozzon létre a CODE_METRICS.md fájl módosításaival. Ezeket a módosításokat a munkafolyamat-összeállítás részeként kezeli a rendszer.

Az oktatóanyag forráskódjára mutató hivatkozások az alkalmazás bizonyos részeit kihagyják a rövidség kedvéért. A teljes alkalmazáskód elérhető a GitHubon.

Az alkalmazás bemutatása

A .NET-konzolalkalmazás a CommandLineParser NuGet-csomag használatával elemzi az argumentumokat az ActionInputs objektumban.

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

Az előző műveletbemeneti osztály több szükséges bemenetet határoz meg az alkalmazás sikeres futtatásához. A konstruktor megírja a "GREETINGS" környezeti változó értékét, ha az aktuális végrehajtási környezetben elérhető. A Name rendszer elemzi és Branch hozzárendeli a tulajdonságokat egy tagolt sztring "/" utolsó szegmenséből.

A definiált műveletbeviteli osztályban a Program.cs fájlra kell összpontosítani.

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

A Program fájl egyszerűbb a rövidség kedvéért, hogy megismerje a teljes mintaforrást, lásd: Program.cs. A mechanika a használathoz szükséges kazánlemez-kódot mutatja be:

Külső projekt- vagy csomaghivatkozások használhatók, és függőséginjektálással regisztrálhatók. Ez Get<TService> egy statikus helyi függvény, amely megköveteli a IHost példányt, és a szükséges szolgáltatások feloldására szolgál. CommandLine.Parser.Default A singleton használatával az alkalmazás lekéri a példányt parser a args. Ha az argumentumok nem elemezhetők, az alkalmazás nem nulla kilépési kóddal lép ki. További információ: Kilépési kódok beállítása műveletekhez.

Az argek sikeres elemzésekor az alkalmazás helyesen lett meghívva a szükséges bemenetekkel. Ebben az esetben az elsődleges funkció StartAnalysisAsync hívása történik.

Kimeneti értékek írásához a GitHub Actions által felismert formátumot kell követnie: Kimeneti paraméter beállítása.

A .NET-alkalmazás előkészítése a GitHub Actionshez

A GitHub Actions az alkalmazásfejlesztés két változatát támogatja, vagy

  • JavaScript (opcionálisan TypeScript)
  • Docker-tároló (bármely, a Dockeren futó alkalmazás)

Előfordulhat, hogy a GitHub-művelet üzemeltetett virtuális környezetében telepítve lehet a .NET. A célkörnyezetben előre telepített adatokról a GitHub Actions Virtuális környezetek című témakörben olvashat bővebben. Bár .NET CLI-parancsokat is futtathat a GitHub Actions munkafolyamataiból, a teljesebb működés érdekében. NET-alapú GitHub Action, javasoljuk, hogy tárolóba helyezi az alkalmazást. További információ: .NET-alkalmazás tárolóba helyezése.

A Dockerfile

A Dockerfile a rendszerképek létrehozásához szükséges utasítások készlete. .NET-alkalmazások esetén a Dockerfile általában a megoldásfájl melletti könyvtár gyökerében található.

# 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" ]

Megjegyzés:

Az oktatóanyagban szereplő .NET-alkalmazás a .NET SDK-ra támaszkodik a funkciói részeként. A Dockerfile az előzőektől független új Docker-rétegeket hoz létre. Az SDK-lemezképpel kezdődik, és hozzáadja az előző rétegcsoport buildkimenetét. Az olyan alkalmazások esetében, amelyek nem igénylik a .NET SDK-t a működésük részeként, inkább csak a .NET-futtatókörnyezetre kell támaszkodniuk. Ez jelentősen csökkenti a kép méretét.

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

Figyelmeztetés

Ügyeljen a Dockerfile minden lépésére, mivel ez eltér a docker-támogatás hozzáadása funkcióból létrehozott szokásos Docker-fájltól. Az utolsó néhány lépés különösen úgy változik, hogy nem ad meg egy újat WORKDIR , amely megváltoztatná az alkalmazás ENTRYPOINTelérési útját.

Az előző Dockerfile-lépések a következők:

  • Az alaprendszerkép mcr.microsoft.com/dotnet/sdk:7.0 beállítása aliasként build-env.
  • A tartalom másolása és a .NET-alkalmazás közzététele:
    • Az alkalmazás közzététele a dotnet publish paranccsal történik.
  • Címkék alkalmazása a tárolóra.
  • A .NET SDK-rendszerkép továbbítása innen: mcr.microsoft.com/dotnet/sdk:7.0
  • A közzétett buildkimenet másolása a build-env.
  • Annak a belépési pontnak a meghatározása, amely a következőre delegálható dotnet /DotNet.GitHubAction.dll: .

Tipp.

Az MCR a mcr.microsoft.com "Microsoft Container Registry" (Microsoft Container Registry) nevet jelöli, és a Microsoft konzorciális tárolókatalógusa a hivatalos Docker Hubból. További információt a Microsoft syndicates tárolókatalógusában talál.

Figyelem

Ha global.json fájllal rögzíti az SDK-verziót, kifejezetten hivatkoznia kell erre a verzióra a Dockerfile-ban. Ha például global.json használatával rögzítette az SDK-verziót5.0.300, a Dockerfile-nak kell használniamcr.microsoft.com/dotnet/sdk:5.0.300. Ez megakadályozza a GitHub Actions feltörését egy új alverzió kiadásakor.

Műveleti bemenetek és kimenetek definiálása

Az Alkalmazás felfedezése szakaszban megismerhette az osztálytActionInputs. Ez az objektum a GitHub-művelet bemeneteit jelöli. Ahhoz, hogy a GitHub felismerje, hogy az adattár egy GitHub-művelet, rendelkeznie kell egy action.yml fájllal az adattár gyökerénél.

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

Az előző action.yml fájl a következőket határozza meg:

  • A name GitHub-művelet és description a
  • A brandingGitHub Marketplace-en a művelet egyedibb azonosításához használt
  • Az inputs, amely egy-az-egyhez megfelelteti az osztályt ActionInputs
  • A outputsmunkafolyamat-összeállításban Program és annak részeként írt,
  • A runs csomópont, amely közli a GitHubbal, hogy az alkalmazás egy docker alkalmazás, és milyen argumentumokat kell átadni neki

További információ: GitHub Actions metaadatszintaxisa.

Előre definiált környezeti változók

A GitHub Actions használatával alapértelmezés szerint sok környezeti változót fog kapni. A változó GITHUB_REF például mindig tartalmaz egy hivatkozást a munkafolyamat futtatását kiváltó ágra vagy címkére. GITHUB_REPOSITORY tulajdonosi és adattárnévvel rendelkezik, dotnet/docspéldául .

Meg kell vizsgálnia az előre definiált környezeti változókat, és ennek megfelelően kell használnia őket.

Munkafolyamat-összeállítás

A .NET-alkalmazás tárolóba van adva, és a műveleti bemenetek és kimenetek definiálva vannak, készen áll a művelet felhasználására. A GitHub Actionst nem kell közzétenni a GitHub Marketplace-en a használathoz. A munkafolyamatok yaML-fájlokként vannak definiálva az adattár .github/workflows könyvtárában.

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

Fontos

Tárolóalapú GitHub Actions esetén a következőket kell használnia runs-on: ubuntu-latest: . További információ: Munkafolyamat szintaxisa jobs.<job_id>.runs-on.

Az előző munkafolyamat YAML-fájlja három elsődleges csomópontot határoz meg:

  • A name munkafolyamat. Ez a név használatos munkafolyamat-állapotjelvény létrehozásakor is.
  • A on csomópont határozza meg, hogy mikor és hogyan aktiválódik a művelet.
  • A jobs csomópont az egyes feladatok különböző feladatait és lépéseit tagolja. Az egyes lépések a GitHub Actionst használják fel.

További információ: Az első munkafolyamat létrehozása.

A csomópontra összpontosítva steps a kompozíció nyilvánvalóbb:

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

Ez jobs.steps a munkafolyamat-összetételt jelöli. A lépések úgy vannak vezénylve, hogy szekvenciálisak, kommunikatívak és összeállíthatók legyenek. A lépések különböző GitHub Actions-műveletekkel, amelyek mindegyike bemenetekkel és kimenetekkel rendelkezik, munkafolyamatok is összeállíthatók.

Az előző lépésekben a következőket figyelheti meg:

  1. Az adattár ki van véve.

  2. Manuálisan futtatott üzenet lesz kinyomtatva a munkafolyamat-naplóba.

  3. A következőként dotnet-code-metricsazonosított lépés:

    • uses: dotnet/samples/github-actions/DotNet.GitHubAction@main a tárolóalapú .NET-alkalmazás helye ebben az oktatóanyagban.
    • env létrehoz egy környezeti változót "GREETING", amely az alkalmazás végrehajtása során lesz kinyomtatva.
    • with az egyes szükséges műveletbemeneteket adja meg.
  4. Egy elnevezett Create pull request feltételes lépés akkor fut, ha a dotnet-code-metrics lépés egy kimeneti paramétert updated-metrics ad meg, amelynek értéke a következő true.

Fontos

A GitHub lehetővé teszi titkosított titkos kódok létrehozását. A titkos kulcsok a munkafolyamat-összeállításban használhatók a ${{ secrets.SECRET_NAME }} szintaxis használatával. A GitHub-művelet kontextusában van egy GitHub-jogkivonat, amely alapértelmezés szerint automatikusan fel van töltve: ${{ secrets.GITHUB_TOKEN }}. További információ: A GitHub Actions környezet- és kifejezésszintaxisa.

Az alkalmazás összeállítása

A dotnet/samples GitHub-adattár számos .NET-mintaforráskód-projektnek ad otthont, beleértve az oktatóanyagban szereplő alkalmazást is.

A létrehozott CODE_METRICS.md fájl navigálásra alkalmas. Ez a fájl az elemzett projektek hierarchiáját jelöli. Minden projekt rendelkezik egy legfelső szintű szakaszsal és egy hangulatjelekkel, amelyek a beágyazott objektumok legmagasabb ciklomatikus összetettségének általános állapotát képviselik. A fájlban való navigálás során minden szakasz részletezési lehetőségeket tesz elérhetővé az egyes területek összegzésével. A Markdown összecsukható szakaszokkal rendelkezik, mint további kényelem.

A hierarchia a következőből halad előre:

  • Projektfájl szerelvényhez
  • Szerelvény a névtérbe
  • Névtér nevesített típushoz
  • Minden névvel ellátott típushoz tartozik egy tábla, és mindegyik táblához tartozik:
    • Mezők, metódusok és tulajdonságok sorszámaira mutató hivatkozások
    • Egyedi minősítések kódmetrikákhoz

Működés közben

A munkafolyamat azt határozza meg, hogy onpush az main ágra irányuló művelet futtatása aktiválva legyen. Amikor fut, a GitHub Műveletek lapja jelenti a végrehajtás élő naplóstreamjét. Íme egy példanapló a .NET code metrics futtatásból:

.NET code metrics - GitHub Actions log

Teljesítménynövelő fejlesztések

Ha követte a mintát, észrevehette, hogy minden alkalommal, amikor ezt a műveletet használja, docker-buildet készít a rendszerképhez. Így minden eseményindítónak van egy kis ideje a tároló összeállítására a futtatás előtt. Mielőtt közzétenné a GitHub Actionst a piactéren, a következőket kell elvégeznie:

  1. (automatikusan) A Docker-rendszerkép létrehozása
  2. A Docker-rendszerkép leküldése a GitHub Container Registrybe (vagy bármely más nyilvános tárolóregisztrációs adatbázisba)
  3. Módosítsa a műveletet úgy, hogy ne a rendszerképet hozza létre, hanem egy nyilvános beállításjegyzékből származó lemezképet használjon.
# 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!!

További információ: GitHub Docs: A tárolóregisztrációs adatbázis használata.

Kapcsolódó információk

További lépések