Samouczek: tworzenie akcji usługi GitHub za pomocą platformy .NET
Dowiedz się, jak utworzyć aplikację platformy .NET, która może być używana jako akcja usługi GitHub. Funkcja GitHub Actions umożliwia automatyzację i kompozycję przepływu pracy. Za pomocą funkcji GitHub Actions można tworzyć, testować i wdrażać kod źródłowy z usługi GitHub. Ponadto akcje uwidaczniają możliwość programowej interakcji z problemami, tworzenia żądań ściągnięcia, przeprowadzania przeglądów kodu i zarządzania gałęziami. Aby uzyskać więcej informacji na temat ciągłej integracji z funkcją GitHub Actions, zobacz Kompilowanie i testowanie platformy .NET.
Z tego samouczka dowiesz się, jak wykonywać następujące czynności:
- Przygotowywanie aplikacji .NET dla funkcji GitHub Actions
- Definiowanie danych wejściowych i wyjściowych akcji
- Tworzenie przepływu pracy
Wymagania wstępne
- Konto usługi GitHub
- Zestaw .NET 6 SDK lub nowszy
- Zintegrowane środowisko projektowe (IDE) platformy .NET
- Możesz korzystać z środowiska IDE programu Visual Studio
Intencja aplikacji
Aplikacja w tym samouczku wykonuje analizę metryk kodu przez:
Skanowanie i odnajdywanie plików projektów *.csproj i *.vbproj .
Analizowanie odnalezionego kodu źródłowego w następujących projektach:
- Złożoność cyklotyczna
- Indeks możliwości konserwacji
- Głębokość dziedziczenia
- Sprzęganie klas
- Liczba wierszy kodu źródłowego
- Przybliżone wiersze kodu wykonywalnego
Tworzenie (lub aktualizowanie) pliku CODE_METRICS.md .
Aplikacja nie jest odpowiedzialna za utworzenie żądania ściągnięcia ze zmianami w pliku CODE_METRICS.md. Te zmiany są zarządzane w ramach kompozycji przepływu pracy.
Odwołania do kodu źródłowego w tym samouczku zawierają fragmenty aplikacji pominięte w celu zwięzłości. Pełny kod aplikacji jest dostępny w witrynie GitHub.
Eksplorowanie aplikacji
Aplikacja konsolowa platformy .NET używa CommandLineParser
pakietu NuGet do analizowania argumentów w ActionInputs
obiekcie.
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]);
}
}
}
Poprzednia klasa danych wejściowych akcji definiuje kilka wymaganych danych wejściowych, aby aplikacja została pomyślnie uruchomiona. Konstruktor zapisze wartość zmiennej środowiskowej "GREETINGS"
, jeśli jest ona dostępna w bieżącym środowisku wykonywania. Właściwości Name
i Branch
są analizowane i przypisywane z ostatniego segmentu "/"
rozdzielanego ciągu.
Po zdefiniowaniu klasy akcji skoncentruj się na pliku 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);
}
Plik Program
jest uproszczony w celu zwięzłości, aby zapoznać się z pełnym źródłem przykładu, zobacz Program.cs. Mechanika w miejscu demonstruje standardowy kod wymagany do użycia:
Można użyć odwołań do projektu zewnętrznego lub pakietu i zarejestrować się za pomocą wstrzykiwania zależności. Jest Get<TService>
to statyczna funkcja lokalna, która wymaga wystąpienia i służy do rozwiązywania IHost
wymaganych usług. W przypadku pojedynczego CommandLine.Parser.Default
wystąpienia aplikacja pobiera parser
wystąpienie z klasy args
. Gdy argumenty nie mogą być analizowane, aplikacja kończy działanie z kodem zakończenia bez zera. Aby uzyskać więcej informacji, zobacz Ustawianie kodów zakończenia dla akcji.
Po pomyślnym przeanalizowaniu argumentów aplikacja została wywołana poprawnie z wymaganymi danymi wejściowymi. W takim przypadku jest wykonywane wywołanie funkcji podstawowej StartAnalysisAsync
.
Aby zapisać wartości wyjściowe, należy postępować zgodnie z formatem rozpoznawanym przez funkcję GitHub Actions: ustawianie parametru wyjściowego.
Przygotowywanie aplikacji .NET dla funkcji GitHub Actions
Funkcja GitHub Actions obsługuje dwie odmiany tworzenia aplikacji, albo
- JavaScript (opcjonalnie TypeScript)
- Kontener platformy Docker (dowolna aplikacja działająca na platformie Docker)
Środowisko wirtualne, w którym jest hostowana akcja Usługi GitHub, może lub nie ma zainstalowanego platformy .NET. Aby uzyskać informacje o tym, co jest wstępnie zainstalowane w środowisku docelowym, zobacz GitHub Actions Virtual Environments (Środowiska wirtualne funkcji GitHub Actions). Chociaż można uruchamiać polecenia interfejsu wiersza polecenia platformy .NET z przepływów pracy funkcji GitHub Actions, aby uzyskać bardziej funkcjonalne polecenie . Akcja usługi GitHub oparta na platformie NET zaleca się konteneryzowanie aplikacji. Aby uzyskać więcej informacji, zobacz Containerize a .NET app (Konteneryzowanie aplikacji platformy .NET).
Plik Dockerfile
Plik Dockerfile to zestaw instrukcji tworzenia obrazu. W przypadku aplikacji .NET plik Dockerfile zwykle znajduje się w katalogu głównym katalogu obok pliku rozwiązania.
# 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" ]
Uwaga
Aplikacja .NET w tym samouczku opiera się na zestawie SDK platformy .NET w ramach jej funkcji. Plik Dockerfile tworzy nowy zestaw warstw platformy Docker niezależnie od poprzednich. Rozpoczyna się od podstaw przy użyciu obrazu zestawu SDK i dodaje dane wyjściowe kompilacji z poprzedniego zestawu warstw. W przypadku aplikacji, które nie wymagają zestawu SDK platformy .NET w ramach ich funkcji, należy zamiast tego polegać tylko na środowisku uruchomieniowym platformy .NET. Znacznie zmniejsza to rozmiar obrazu.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Ostrzeżenie
Zwróć szczególną uwagę na każdy krok w pliku Dockerfile, ponieważ różni się on od standardowego pliku Dockerfile utworzonego na podstawie funkcji "dodaj obsługę platformy Docker". W szczególności kilka ostatnich kroków różni się, nie określając nowego WORKDIR
, co spowoduje zmianę ścieżki do aplikacji ENTRYPOINT
.
Powyższe kroki pliku Dockerfile obejmują:
- Ustawianie obrazu podstawowego z
mcr.microsoft.com/dotnet/sdk:7.0
jako aliasubuild-env
. - Kopiowanie zawartości i publikowanie aplikacji .NET:
- Aplikacja jest publikowana przy użyciu
dotnet publish
polecenia .
- Aplikacja jest publikowana przy użyciu
- Stosowanie etykiet do kontenera.
- Przekazywanie obrazu zestawu SDK platformy .NET z
mcr.microsoft.com/dotnet/sdk:7.0
- Skopiowanie danych wyjściowych opublikowanej kompilacji z pliku
build-env
. - Definiowanie punktu wejścia, który deleguje do elementu
dotnet /DotNet.GitHubAction.dll
.
Napiwek
McR to mcr.microsoft.com
skrót od "Microsoft Container Registry" (Rejestr kontenerów firmy Microsoft) i jest katalogiem kontenerów syndyka firmy Microsoft z oficjalnego centrum Platformy Docker. Aby uzyskać więcej informacji, zobacz Katalog kontenerów syndykates firmy Microsoft.
Uwaga
Jeśli używasz pliku global.json do przypinania wersji zestawu SDK, należy jawnie odwołać się do tej wersji w pliku Dockerfile. Jeśli na przykład użyto pliku global.json do przypinania wersji 5.0.300
zestawu SDK, plik Dockerfile powinien używać polecenia mcr.microsoft.com/dotnet/sdk:5.0.300
. Zapobiega to uszkodzeniu funkcji GitHub Actions po wydaniu nowej poprawki pomocniczej.
Definiowanie danych wejściowych i wyjściowych akcji
W sekcji Eksploruj aplikację przedstawiono klasę ActionInputs
. Ten obiekt reprezentuje dane wejściowe akcji usługi GitHub. Aby usługa GitHub rozpoznała, że repozytorium jest akcją usługi GitHub, musisz mieć plik action.yml w katalogu głównym repozytorium.
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 }}
Powyższy plik action.yml definiuje:
- Akcja
name
idescription
usługi GitHub - Element
branding
, który jest używany w witrynie GitHub Marketplace , aby ułatwić bardziej unikatowe zidentyfikowanie akcji - ,
inputs
który mapuje jeden do jednego z klasąActionInputs
- Element
outputs
, który jest zapisywany w obiekcieProgram
i używany jako część kompozycji przepływu pracy - Węzeł
runs
, który informuje usługę GitHub, że aplikacja jest aplikacjądocker
i jakie argumenty mają być przekazywane do niej
Aby uzyskać więcej informacji, zobacz Składnia metadanych dla funkcji GitHub Actions.
Wstępnie zdefiniowane zmienne środowiskowe
Dzięki funkcji GitHub Actions domyślnie uzyskasz wiele zmiennych środowiskowych. Na przykład zmienna GITHUB_REF
będzie zawsze zawierać odwołanie do gałęzi lub tagu, który wyzwolił przebieg przepływu pracy. GITHUB_REPOSITORY
ma nazwę właściciela i repozytorium, na przykład dotnet/docs
.
Należy zapoznać się ze wstępnie zdefiniowanymi zmiennymi środowiskowymi i użyć ich odpowiednio.
Kompozycja przepływu pracy
Po konteneryzowanej aplikacji .NET oraz zdefiniowanych danych wejściowych i wyjściowych akcji możesz przystąpić do korzystania z akcji. Do użycia w witrynie GitHub Marketplace nie trzeba publikować funkcji GitHub Actions. Przepływy pracy są definiowane w katalogu .github/workflows repozytorium jako pliki 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.'
Ważne
W przypadku konteneryzowanych funkcji GitHub Actions wymagane jest użycie polecenia runs-on: ubuntu-latest
. Aby uzyskać więcej informacji, zobacz Składnia jobs.<job_id>.runs-on
przepływu pracy .
Powyższy plik YAML przepływu pracy definiuje trzy węzły podstawowe:
- Przepływ
name
pracy. Ta nazwa jest również używana podczas tworzenia wskaźnika stanu przepływu pracy. - Węzeł
on
definiuje, kiedy i jak jest wyzwalana akcja. - Węzeł
jobs
przedstawia różne zadania i kroki w ramach każdego zadania. Poszczególne kroki korzystają z funkcji GitHub Actions.
Aby uzyskać więcej informacji, zobacz Tworzenie pierwszego przepływu pracy.
Skupienie się na węźle steps
jest bardziej oczywiste:
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.'
Obiekt jobs.steps
reprezentuje kompozycję przepływu pracy. Kroki są orkiestrowane w taki sposób, że są sekwencyjne, komunikatywne i komponowalne. W przypadku różnych funkcji GitHub Actions reprezentujących kroki każdy z nich ma dane wejściowe i wyjściowe, można tworzyć przepływy pracy.
W poprzednich krokach można obserwować:
Repozytorium zostało wyewidencjonowane.
Komunikat jest drukowany w dzienniku przepływu pracy po ręcznym uruchomieniu.
Krok zidentyfikowany jako
dotnet-code-metrics
:uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
to lokalizacja konteneryzowanej aplikacji .NET w tym samouczku.env
Tworzy zmienną środowiskową"GREETING"
, która jest drukowana podczas wykonywania aplikacji.with
określa każde z wymaganych danych wejściowych akcji.
Krok warunkowy o nazwie
Create pull request
jest uruchamiany, gdydotnet-code-metrics
krok określa parametrupdated-metrics
wyjściowy z wartościątrue
.
Ważne
Usługa GitHub umożliwia tworzenie zaszyfrowanych wpisów tajnych. Wpisy tajne mogą być używane w kompozycji przepływu pracy przy użyciu ${{ secrets.SECRET_NAME }}
składni. W kontekście akcji usługi GitHub istnieje token usługi GitHub, który jest domyślnie wypełniany automatycznie: ${{ secrets.GITHUB_TOKEN }}
. Aby uzyskać więcej informacji, zobacz Składnia kontekstu i wyrażenia dla funkcji GitHub Actions.
Zebranie wszystkich elementów
Repozytorium dotnet/samples w witrynie GitHub znajduje się w wielu przykładowych projektach kodu źródłowego platformy .NET, w tym aplikacji w tym samouczku.
Wygenerowany plik CODE_METRICS.md można nawigowalne. Ten plik reprezentuje hierarchię analizowanych projektów. Każdy projekt ma sekcję najwyższego poziomu i emoji reprezentujące ogólny stan najwyższej złożoności cyklatycznej dla zagnieżdżonych obiektów. Podczas nawigowania po pliku każda sekcja uwidacznia możliwości przechodzenia do szczegółów z podsumowaniem każdego obszaru. Znaczniki markdown mają zwijane sekcje jako dodatkową wygodę.
Hierarchia postępuje z:
- Plik projektu do zestawu
- Zestaw do przestrzeni nazw
- Przestrzeń nazw do nazwanego typu
- Każdy nazwany typ ma tabelę, a każda tabela ma następujące elementy:
- Łącza do numerów wierszy dla pól, metod i właściwości
- Indywidualne klasyfikacje metryk kodu
Działanie
Przepływ pracy określa, że on
do push
main
gałęzi akcja jest wyzwalana do uruchomienia. Po uruchomieniu karta Akcje w usłudze GitHub będzie raportować strumień dziennika na żywo jego wykonywania. Oto przykładowy dziennik z .NET code metrics
przebiegu:
usprawnienia dotyczące wydajności
Jeśli wykonano czynności opisane w przykładzie, być może zauważysz, że za każdym razem, gdy ta akcja jest używana, zostanie utworzona kompilacja platformy Docker dla tego obrazu. Dlatego każdy wyzwalacz ma jakiś czas na skompilowanie kontenera przed jego uruchomieniem. Przed udostępnieniem funkcji GitHub Actions na platformie handlowej należy wykonać następujące czynności:
- (automatycznie) Kompilowanie obrazu platformy Docker
- Wypchnięcie obrazu platformy Docker do usługi GitHub Container Registry (lub dowolnego innego publicznego rejestru kontenerów)
- Zmień akcję, aby nie skompilować obrazu, ale użyć obrazu z rejestru publicznego.
# 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!!
Aby uzyskać więcej informacji, zobacz GitHub Docs: Praca z rejestrem kontenerów.
Zobacz też
- Host ogólny platformy .NET
- Wstrzykiwanie zależności na platformie .NET
- Wartości metryk kodu
- Kompilacja akcji usługi GitHub typu open source na platformie .NET z przepływem pracy umożliwiającym automatyczne kompilowanie i wypychanie obrazu platformy Docker.