Zelfstudie: Een GitHub-actie maken met .NET
Meer informatie over het maken van een .NET-app die kan worden gebruikt als een GitHub Action. GitHub Actions maken werkstroomautomatisering en -samenstelling mogelijk. Met GitHub Actions kunt u broncode bouwen, testen en implementeren vanuit GitHub. Daarnaast bieden acties de mogelijkheid om programmatisch te communiceren met problemen, pull-aanvragen te maken, codebeoordelingen uit te voeren en vertakkingen te beheren. Zie .NET bouwen en testen voor meer informatie over continue integratie met GitHub Actions.
In deze zelfstudie leert u het volgende:
- Een .NET-app voorbereiden voor GitHub Actions
- Actie-invoer en -uitvoer definiëren
- Een werkstroom opstellen
Vereisten
- Een GitHub-account
- De .NET 6 SDK of hoger
- Een .NET Integrated Development Environment (IDE)
- U kunt de Visual Studio IDE gerust gebruiken
De intentie van de app
De app in deze zelfstudie voert metrische codeanalyse uit op:
Scannen en detecteren van projectbestanden *.csproj en *.vbproj .
De gedetecteerde broncode in deze projecten analyseren voor:
- Cyclomatische complexiteit
- Index voor onderhoudbaarheid
- Diepte van overname
- Klassekoppeling
- Aantal regels broncode
- Geschatte regels uitvoerbare code
Een CODE_METRICS.md-bestand maken (of bijwerken).
De app is niet verantwoordelijk voor het maken van een pull-aanvraag met de wijzigingen in het bestand CODE_METRICS.md . Deze wijzigingen worden beheerd als onderdeel van de werkstroomsamenstelling.
Verwijzingen naar de broncode in deze zelfstudie bevatten gedeelten van de app die ter beknoptheid zijn weggelaten. De volledige app-code is beschikbaar op GitHub.
De app verkennen
De .NET-console-app maakt gebruik van het CommandLineParser
NuGet-pakket om argumenten in het ActionInputs
object te parseren.
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]);
}
}
}
De voorgaande actie-invoerklasse definieert verschillende vereiste invoer voor de app om te worden uitgevoerd. De constructor schrijft de waarde van de "GREETINGS"
omgevingsvariabele als deze beschikbaar is in de huidige uitvoeringsomgeving. De Name
en Branch
eigenschappen worden geparseerd en toegewezen vanuit het laatste segment van een "/"
gescheiden tekenreeks.
Met de gedefinieerde actie-invoerklasse richt u zich op het bestand 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);
}
Het Program
bestand is vereenvoudigd voor beknoptheid, om de volledige voorbeeldbron te verkennen, zie Program.cs. De monteurs laten de standaardcode zien die vereist is voor gebruik:
Externe project- of pakketverwijzingen kunnen worden gebruikt en geregistreerd bij afhankelijkheidsinjectie. Het Get<TService>
is een statische lokale functie, waarvoor het IHost
exemplaar is vereist en wordt gebruikt om vereiste services op te lossen. Met de CommandLine.Parser.Default
singleton haalt de app een parser
exemplaar op van de args
. Wanneer de argumenten niet kunnen worden geparseerd, wordt de app afgesloten met een afsluitcode die niet nul is. Zie Afsluitcodes instellen voor acties voor meer informatie.
Wanneer de argumenten zijn geparseerd, werd de app correct aangeroepen met de vereiste invoer. In dit geval wordt een aanroep naar de primaire functionaliteit StartAnalysisAsync
uitgevoerd.
Als u uitvoerwaarden wilt schrijven, moet u de indeling volgen die wordt herkend door GitHub Actions: een uitvoerparameter instellen.
De .NET-app voorbereiden voor GitHub Actions
GitHub Actions ondersteunen twee variaties van app-ontwikkeling, ofwel
- JavaScript (optioneel TypeScript)
- Docker-container (elke app die wordt uitgevoerd in Docker)
De virtuele omgeving waarop de GitHub Action wordt gehost, heeft .NET al dan niet geïnstalleerd. Zie GitHub Actions Virtual Environments voor informatie over wat vooraf is geïnstalleerd in de doelomgeving. Hoewel het mogelijk is om .NET CLI-opdrachten uit te voeren vanuit de GitHub Actions-werkstromen, voor een volledigere werking. GitHub Action op basis van NET raden we u aan om de app in een container te plaatsen. Zie Een .NET-app containeriseren voor meer informatie.
Het Dockerfile
Een Dockerfile is een set instructies voor het bouwen van een installatiekopieën. Voor .NET-toepassingen bevindt het Dockerfile zich meestal in de hoofdmap van de map naast een oplossingsbestand.
# 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" ]
Notitie
De .NET-app in deze zelfstudie is afhankelijk van de .NET SDK als onderdeel van de functionaliteit. Het Dockerfile maakt een nieuwe set Docker-lagen, onafhankelijk van de vorige lagen. Het begint helemaal opnieuw met de SDK-installatiekopieën en voegt de build-uitvoer van de vorige set lagen toe. Voor toepassingen die de .NET SDK niet nodig hebben als onderdeel van hun functionaliteit, moeten ze alleen afhankelijk zijn van de .NET Runtime. Dit vermindert de grootte van de afbeelding aanzienlijk.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Waarschuwing
Let goed op elke stap in het Dockerfile, omdat deze verschilt van de standaard Dockerfile die is gemaakt met de functionaliteit Docker-ondersteuning toevoegen. In het bijzonder variëren de laatste stappen door geen nieuwe WORKDIR
op te geven die het pad naar de app ENTRYPOINT
zou wijzigen.
De voorgaande Dockerfile-stappen omvatten:
- De basisinstallatiekopieën
mcr.microsoft.com/dotnet/sdk:7.0
instellen als de aliasbuild-env
. - De inhoud kopiëren en de .NET-app publiceren:
- De app wordt gepubliceerd met behulp van de
dotnet publish
opdracht.
- De app wordt gepubliceerd met behulp van de
- Labels toepassen op de container.
- De .NET SDK-installatiekopieën van
mcr.microsoft.com/dotnet/sdk:7.0
- De gepubliceerde build-uitvoer van de
build-env
. - Het beginpunt definiëren, waarnaar wordt
dotnet /DotNet.GitHubAction.dll
gedelegeerd.
Tip
De MCR in mcr.microsoft.com
staat voor 'Microsoft Container Registry' en is de gesyndiceerde containercatalogus van Microsoft van de officiële Docker-hub. Zie de containercatalogus van Microsoft syndicates voor meer informatie.
Let op
Als u een global.json-bestand gebruikt om de SDK-versie vast te maken, moet u expliciet naar die versie verwijzen in uw Dockerfile. Als u bijvoorbeeld global.json hebt gebruikt om sdk-versie 5.0.300
vast te maken, moet uw Dockerfile worden gebruiktmcr.microsoft.com/dotnet/sdk:5.0.300
. Dit voorkomt dat de GitHub Actions worden onderbroken wanneer er een nieuwe kleine revisie wordt uitgebracht.
Actie-invoer en -uitvoer definiëren
In de sectie De app verkennen hebt u meer geleerd over de ActionInputs
klas. Dit object vertegenwoordigt de invoer voor de GitHub-actie. GitHub kan alleen herkennen dat de opslagplaats een GitHub Action is. U moet een action.yml-bestand hebben in de hoofdmap van de opslagplaats.
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 }}
Het voorgaande bestand action.yml definieert:
- De
name
endescription
van de GitHub Action - De
branding
, die wordt gebruikt in de GitHub Marketplace om uw actie unieker te identificeren - De
inputs
, waarmee een-op-een met deActionInputs
klasse wordt toegewezen - De
outputs
, waarnaar wordt geschreven in deProgram
en wordt gebruikt als onderdeel van werkstroomsamenstelling - Het
runs
knooppunt, dat GitHub vertelt dat de app eendocker
toepassing is en welke argumenten aan de app moeten worden doorgegeven
Zie De syntaxis van metagegevens voor GitHub Actions voor meer informatie.
Vooraf gedefinieerde omgevingsvariabelen
Met GitHub Actions krijgt u standaard veel omgevingsvariabelen . De variabele GITHUB_REF
bevat bijvoorbeeld altijd een verwijzing naar de vertakking of tag die de werkstroomuitvoering heeft geactiveerd. GITHUB_REPOSITORY
heeft de naam van de eigenaar en de opslagplaats, dotnet/docs
bijvoorbeeld.
U moet de vooraf gedefinieerde omgevingsvariabelen verkennen en dienovereenkomstig gebruiken.
Werkstroomsamenstelling
Nu de .NET-app is gecontaineriseerd en de actie-invoer en -uitvoer zijn gedefinieerd, kunt u de actie gebruiken. GitHub Actions hoeven niet te worden gepubliceerd in de GitHub Marketplace die moet worden gebruikt. Werkstromen worden gedefinieerd in de map .github/workflows van een opslagplaats als YAML-bestanden.
# 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.'
Belangrijk
Voor in containers geplaatste GitHub Actions moet u gebruiken runs-on: ubuntu-latest
. Zie Werkstroomsyntaxis jobs.<job_id>.runs-on
voor meer informatie.
In het voorgaande YAML-werkstroombestand worden drie primaire knooppunten gedefinieerd:
- De
name
werkstroom. Deze naam wordt ook gebruikt bij het maken van een werkstroomstatusbadge. - Het
on
knooppunt definieert wanneer en hoe de actie wordt geactiveerd. - Het
jobs
knooppunt bevat een overzicht van de verschillende taken en stappen binnen elke taak. Afzonderlijke stappen verbruiken GitHub Actions.
Zie Uw eerste werkstroom maken voor meer informatie.
Focus op het steps
knooppunt, de samenstelling is duidelijker:
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.'
De jobs.steps
vertegenwoordigt de werkstroomsamenstelling. Stappen worden zodanig ingedeeld dat ze opeenvolgend, communicatief en samenstelbaar zijn. Met verschillende GitHub Actions die stappen vertegenwoordigen, elk met invoer en uitvoer, kunnen werkstromen worden samengesteld.
In de voorgaande stappen kunt u het volgende bekijken:
De opslagplaats is uitgecheckt.
Er wordt een bericht afgedrukt naar het werkstroomlogboek wanneer het handmatig wordt uitgevoerd.
Een stap die is geïdentificeerd als
dotnet-code-metrics
:uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
is de locatie van de in containers geplaatste .NET-app in deze zelfstudie.env
maakt een omgevingsvariabele"GREETING"
die wordt afgedrukt in de uitvoering van de app.with
geeft elk van de vereiste actie-invoer.
Een voorwaardelijke stap, benoemd
Create pull request
, wordt uitgevoerd wanneer dedotnet-code-metrics
stap een uitvoerparameterupdated-metrics
van met een waarde vantrue
.
Belangrijk
Met GitHub kunt u versleutelde geheimen maken. Geheimen kunnen worden gebruikt in werkstroomsamenstelling, met behulp van de ${{ secrets.SECRET_NAME }}
syntaxis. In de context van een GitHub Action is er een GitHub-token dat standaard automatisch wordt ingevuld: ${{ secrets.GITHUB_TOKEN }}
Zie context- en expressiesyntaxis voor GitHub Actions voor meer informatie.
Alles samenvoegen
De GitHub-opslagplaats dotnet/samples is de thuisbasis van veel .NET-voorbeeldbroncodeprojecten, waaronder de app in deze zelfstudie.
Het gegenereerde bestand CODE_METRICS.md is bevaarbaar. Dit bestand vertegenwoordigt de hiërarchie van de projecten die worden geanalyseerd. Elk project heeft een sectie op het hoogste niveau en een emoji die de algehele status van de hoogste cyclomatische complexiteit voor geneste objecten vertegenwoordigt. Wanneer u door het bestand navigeert, worden in elke sectie inzoommogelijkheden weergegeven met een samenvatting van elk gebied. Markdown heeft samenvouwbare secties als extra gemak.
De hiërarchie wordt voortgezet van:
- Projectbestand naar assembly
- Assembly naar naamruimte
- Naamruimte naar benoemd-type
- Elk benoemd type heeft een tabel en elke tabel heeft:
- Koppelingen naar regelnummers voor velden, methoden en eigenschappen
- Afzonderlijke classificaties voor metrische codegegevens
In actie
De werkstroom geeft aan dat on
een push
naar de main
vertakking, de actie wordt geactiveerd om uit te voeren. Wanneer het wordt uitgevoerd, rapporteert het tabblad Acties in GitHub de livelogboekstream van de uitvoering. Hier volgt een voorbeeldlogboek van de .NET code metrics
uitvoering:
Prestatieverbeteringen
Als u het voorbeeld hebt gevolgd, hebt u misschien gemerkt dat elke keer dat deze actie wordt gebruikt, een Docker-build voor die installatiekopieën uitvoert. Dus, elke trigger wordt geconfronteerd met enige tijd om de container te bouwen voordat deze wordt uitgevoerd. Voordat u uw GitHub Actions publiceert in de marketplace, moet u het volgende doen:
- (automatisch) De Docker-installatiekopieën bouwen
- Push de docker-installatiekopieën naar het GitHub-containerregister (of een ander openbaar containerregister)
- Wijzig de actie om de installatiekopieën niet te bouwen, maar om een installatiekopieën uit een openbaar register te gebruiken.
# 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!!
Zie GitHub Docs voor meer informatie: Werken met het containerregister.
Zie ook
- .NET Generic Host
- Afhankelijkheidsinjectie in .NET
- Waarden voor metrische gegevens code
- Opensource GitHub Action-build in .NET met een werkstroom voor het automatisch bouwen en pushen van de Docker-installatiekopieën.