Självstudie: Skapa en GitHub-åtgärd med .NET
Lär dig hur du skapar en .NET-app som kan användas som en GitHub-åtgärd. GitHub Actions möjliggör automatisering och sammansättning av arbetsflöden. Med GitHub Actions kan du skapa, testa och distribuera källkod från GitHub. Dessutom visar åtgärder möjligheten att programmatiskt interagera med problem, skapa pull-begäranden, utföra kodgranskningar och hantera grenar. Mer information om kontinuerlig integrering med GitHub Actions finns i Skapa och testa .NET.
I den här självstudien lär du dig att:
- Förbereda en .NET-app för GitHub Actions
- Definiera åtgärdsindata och utdata
- Skapa ett arbetsflöde
Förutsättningar
- Ett GitHub-konto
- .NET 6 SDK eller senare
- En .NET-integrerad utvecklingsmiljö (IDE)
Avsikten med appen
Appen i den här självstudien utför kodmåttanalys genom att:
Skanna och identifiera *.csproj - och *.vbproj-projektfiler .
Analysera den identifierade källkoden i dessa projekt för:
- Cyklomatisk komplexitet
- Underhållsindex
- Arvsdjup
- Klasskoppling
- Antal rader med källkod
- Ungefärliga rader med körbar kod
Skapa (eller uppdatera) en CODE_METRICS.md-fil .
Appen ansvarar inte för att skapa en pull-begäran med ändringarna i filen CODE_METRICS.md . Dessa ändringar hanteras som en del av arbetsflödets sammansättning.
Referenser till källkoden i den här självstudien innehåller delar av appen utelämnade för korthet. Den fullständiga appkoden är tillgänglig på GitHub.
Utforska appen
.NET-konsolappen CommandLineParser
använder NuGet-paketet för att parsa argument i ActionInputs
objektet.
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]);
}
}
}
Föregående åtgärdsindataklass definierar flera nödvändiga indata för att appen ska kunna köras. Konstruktorn skriver "GREETINGS"
miljövariabelvärdet om ett är tillgängligt i den aktuella körningsmiljön. Name
Egenskaperna och Branch
parsas och tilldelas från det sista segmentet i en "/"
avgränsad sträng.
Med klassen för definierade åtgärdsindata fokuserar du på filen 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);
}
Filen Program
är förenklad för korthet, för att utforska den fullständiga exempelkällan, se Program.cs. Mekaniken på plats visar den pannplåtskod som krävs för att använda:
Externa projekt- eller paketreferenser kan användas och registreras med beroendeinmatning. Get<TService>
är en statisk lokal funktion som kräver instansen IHost
och används för att lösa nödvändiga tjänster. Med singletonen CommandLine.Parser.Default
hämtar appen en parser
instans från args
. När argumenten inte kan parsas avslutas appen med en slutkod som inte är noll. Mer information finns i Ange slutkoder för åtgärder.
När args parsas anropades appen korrekt med nödvändiga indata. I det här fallet görs ett anrop till den primära funktionen StartAnalysisAsync
.
Om du vill skriva utdatavärden måste du följa formatet som identifieras av GitHub Actions: Ange en utdataparameter.
Förbereda .NET-appen för GitHub Actions
GitHub Actions stöder två varianter av apputveckling, antingen
- JavaScript (valfritt TypeScript)
- Docker-container (alla appar som körs på Docker)
Den virtuella miljön där GitHub-åtgärden finns kanske eller kanske inte har .NET installerat. Information om vad som är förinstallerat i målmiljön finns i Virtuella GitHub Actions-miljöer. Även om det är möjligt att köra .NET CLI-kommandon från GitHub Actions-arbetsflöden, för en mer fullt fungerande . NET-baserad GitHub Action rekommenderar vi att du containeriserar appen. Mer information finns i Containerisera en .NET-app.
The Dockerfile
En Dockerfile är en uppsättning instruktioner för att skapa en avbildning. För .NET-program finns Dockerfile vanligtvis i roten i katalogen bredvid en lösningsfil.
# 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" ]
Kommentar
.NET-appen i den här självstudien förlitar sig på .NET SDK som en del av dess funktioner. Dockerfile skapar en ny uppsättning Docker-lager, oberoende av de tidigare. Den börjar från grunden med SDK-avbildningen och lägger till byggutdata från den tidigare uppsättningen lager. För program som inte kräver .NET SDK som en del av deras funktioner bör de bara förlita sig på .NET Runtime i stället. Detta minskar bildens storlek avsevärt.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Varning
Var uppmärksam på varje steg i Dockerfile, eftersom det skiljer sig från standardfunktionerna för Dockerfile som skapats från funktionen "lägg till docker-support". I synnerhet varierar de senaste stegen genom att inte ange en ny WORKDIR
som skulle ändra sökvägen till appens ENTRYPOINT
.
Föregående Dockerfile-steg är:
- Ange basavbildningen från
mcr.microsoft.com/dotnet/sdk:7.0
som aliasbuild-env
. - Kopiera innehållet och publicera .NET-appen:
- Appen publiceras med kommandot
dotnet publish
.
- Appen publiceras med kommandot
- Tillämpa etiketter på containern.
- Vidarebefordra .NET SDK-avbildningen från
mcr.microsoft.com/dotnet/sdk:7.0
- Kopiera publicerade build-utdata från
build-env
. - Definiera startpunkten, som delegerar till
dotnet /DotNet.GitHubAction.dll
.
Dricks
MCR i mcr.microsoft.com
står för "Microsoft Container Registry" och är Microsofts syndikerade containerkatalog från den officiella Docker-hubben. Mer information finns i Containerkatalogen för Microsoft-syndikat.
Varning
Om du använder en global.json-fil för att fästa SDK-versionen bör du uttryckligen referera till den versionen i Din Dockerfile. Om du till exempel har använt global.json för att fästa SDK-versionen 5.0.300
ska Dockerfile använda mcr.microsoft.com/dotnet/sdk:5.0.300
. Detta förhindrar att GitHub Actions bryts när en ny mindre revision släpps.
Definiera åtgärdsindata och utdata
I avsnittet Utforska appen har du lärt dig om ActionInputs
klassen. Det här objektet representerar indata för GitHub-åtgärden. För att GitHub ska kunna känna igen att lagringsplatsen är en GitHub-åtgärd måste du ha en action.yml-fil i roten på lagringsplatsen.
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 }}
Föregående action.yml-fil definierar:
- Och
name
description
för GitHub-åtgärden - ,
branding
som används på GitHub Marketplace för att hjälpa dig att identifiera din åtgärd på ett mer unikt sätt - ,
inputs
som mappar en-till-en medActionInputs
klassen - ,
outputs
som skrivs till i och används som en del av arbetsflödessammansättningenProgram
- Noden
runs
, som talar om för GitHub att appen är ettdocker
program och vilka argument som ska skickas till den
Mer information finns i Metadatasyntax för GitHub Actions.
Fördefinierade miljövariabler
Med GitHub Actions får du som standard många miljövariabler . Variabeln GITHUB_REF
innehåller till exempel alltid en referens till den gren eller tagg som utlöste arbetsflödeskörningen. GITHUB_REPOSITORY
har ägaren och lagringsplatsens namn, dotnet/docs
till exempel .
Du bör utforska de fördefinierade miljövariablerna och använda dem i enlighet med detta.
Arbetsflödessammansättning
När .NET-appen är containerbaserad och åtgärdsindata och utdata har definierats är du redo att använda åtgärden. GitHub Actions behöver inte publiceras på GitHub Marketplace som ska användas. Arbetsflöden definieras i katalogen .github/workflows på en lagringsplats som YAML-filer.
# 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.'
Viktigt!
För github-åtgärder i containrar måste du använda runs-on: ubuntu-latest
. Mer information finns i Arbetsflödessyntax .jobs.<job_id>.runs-on
Yaml-filen för föregående arbetsflöde definierar tre primära noder:
- Arbetsflödets
name
. Det här namnet är också vad som används när du skapar ett statusmärke för arbetsflödet. - Noden
on
definierar när och hur åtgärden utlöses. - Noden
jobs
beskriver de olika jobben och stegen i varje jobb. Enskilda steg använder GitHub Actions.
Mer information finns i Skapa ditt första arbetsflöde.
Med fokus på steps
noden är kompositionen mer uppenbar:
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
Representerar arbetsflödets sammansättning. Stegen samordnas så att de är sekventiella, kommunikativa och komposterbara. Med olika GitHub Actions som representerar steg, där var och en har indata och utdata, kan arbetsflöden bestå.
I föregående steg kan du observera:
Lagringsplatsen är utcheckad.
Ett meddelande skrivs ut till arbetsflödesloggen när det körs manuellt.
Ett steg som identifieras som
dotnet-code-metrics
:uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
är platsen för den containerbaserade .NET-appen i den här självstudien.env
skapar en miljövariabel"GREETING"
, som skrivs ut i körningen av appen.with
anger var och en av de nödvändiga åtgärdsindata.
Ett villkorsstyrt steg med namnet
Create pull request
körs närdotnet-code-metrics
steget anger en utdataparameter förupdated-metrics
med värdettrue
.
Viktigt!
Med GitHub kan du skapa krypterade hemligheter. Hemligheter kan användas i arbetsflödets sammansättning med hjälp av syntaxen ${{ secrets.SECRET_NAME }}
. I kontexten för en GitHub-åtgärd finns det en GitHub-token som fylls i automatiskt som standard: ${{ secrets.GITHUB_TOKEN }}
. Mer information finns i Kontext- och uttryckssyntax för GitHub Actions.
Färdigställa allt
GitHub-lagringsplatsen dotnet/samples är hem för många .NET-exempelkällkodprojekt, inklusive appen i den här självstudien.
Den genererade filen CODE_METRICS.md är navigeringsbar. Den här filen representerar hierarkin för de projekt som den analyserade. Varje projekt har ett avsnitt på den översta nivån och en emoji som representerar den övergripande statusen för den högsta cyklomatiska komplexiteten för kapslade objekt. När du navigerar i filen exponerar varje avsnitt möjligheter för ökad detaljnivå med en sammanfattning av varje område. Markdown har komprimerbara avsnitt som en extra bekvämlighet.
Hierarkin fortsätter från:
- Projektfil till sammansättning
- Sammansättning till namnområde
- Namnområde till namngiven typ
- Varje namngiven typ har en tabell och varje tabell har:
- Länkar till radnummer för fält, metoder och egenskaper
- Individuella klassificeringar för kodmått
Exempel
Arbetsflödet anger att on
en push
till grenen main
, åtgärden utlöses för att köras. När den körs rapporterar fliken Åtgärder i GitHub liveloggströmmen för dess körning. Här är en exempellogg från körningen .NET code metrics
:
Prestandaförbättringar
Om du följde exemplet kanske du har märkt att varje gång den här åtgärden används, kommer den att göra en docker-version för avbildningen. Därför har varje utlösare en viss tid på sig att skapa containern innan den körs. Innan du släpper dina GitHub Actions på marknadsplatsen bör du:
- (automatiskt) Skapa Docker-avbildningen
- Push-överför docker-avbildningen till GitHub Container Registry (eller något annat offentligt containerregister)
- Ändra åtgärden till att inte skapa avbildningen, utan att använda en avbildning från ett offentligt register.
# 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!!
Mer information finns i GitHub Docs: Arbeta med containerregistret.
Se även
- Allmän .NET-värd
- Beroendeinmatning i .NET
- Kodmåttvärden
- GitHub Action-version med öppen källkod i .NET med ett arbetsflöde för att skapa och push-överföra Docker-avbildningen automatiskt.