Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Aprenda a crear una aplicación de .NET que se pueda usar como una acción de GitHub. Acciones de GitHub permiten la automatización y composición del flujo de trabajo. Con Acciones de GitHub, puede compilar, probar e implementar código fuente desde GitHub. Además, las acciones ofrecen la posibilidad de interactuar mediante programación con incidencias, crear pull requests, realizar revisiones de código y administrar ramas. Para obtener más información sobre la integración continua con Acciones de GitHub, consulte Creación y prueba de .NET.
En este tutorial, aprenderá a:
- Preparación de una aplicación .NET para Acciones de GitHub
- Definición de entradas y salidas de acción
- Redacción de un flujo de trabajo
Prerrequisitos
- Una cuenta de GitHub
- El SDK de .NET 6 o posterior
- Un entorno de desarrollo integrado (IDE) de .NET
- No dude en usar el IDE de Visual Studio
La intención de la aplicación
La aplicación de este tutorial realiza el análisis de métricas de código mediante:
Examinar y detectar archivos de proyecto *.csproj y *.vbproj .
Análisis del código fuente detectado en estos proyectos para:
- Complejidad ciclomática
- Índice de mantenimiento
- Profundidad de herencia
- Acoplamiento de clases
- Número de líneas de código fuente
- Líneas aproximadas de código ejecutable
Crear (o actualizar) un archivo CODE_METRICS.md .
La aplicación no es responsable de crear un pull request para los cambios en el archivo CODE_METRICS.md. Estos cambios se administran como parte de la composición del flujo de trabajo.
Las referencias al código fuente de este tutorial tienen partes de la aplicación omitidas para mayor brevedad. El código completo de la aplicación está disponible en GitHub.
Exploración de la aplicación
La aplicación de consola de .NET usa el CommandLineParser paquete NuGet para analizar argumentos en el ActionInputs objeto .
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]);
}
}
}
La clase de entradas de acción anterior define varias entradas necesarias para que la aplicación se ejecute correctamente. El constructor escribirá el valor de la "GREETINGS" variable de entorno, si uno está disponible en el entorno de ejecución actual. Las propiedades Name y Branch se analizan y asignan desde el último segmento de una cadena delimitada por "/".
Con la clase de entradas de acción definida, céntrese en el archivo 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);
}
El Program archivo se simplifica para mayor brevedad, para explorar el origen de ejemplo completo, consulte Program.cs. La mecánica establecida demuestra el código boilerplate necesario para usar:
Se pueden utilizar referencias externas de proyecto o paquete y registrarlas con la inyección de dependencias.
Get<TService> es una función local estática, que requiere la IHost instancia y se usa para resolver los servicios necesarios. Con el CommandLine.Parser.Default singleton, la aplicación obtiene una instancia parser de args. Cuando los argumentos no se pueden analizar, la aplicación se cierra con un código de salida distinto de cero. Para obtener más información, consulte Establecimiento de códigos de salida para acciones.
Cuando los argumentos se analizan correctamente, se llamó a la aplicación correctamente con las entradas necesarias. En este caso, se realiza una llamada a la funcionalidad StartAnalysisAsync principal.
Para escribir valores de salida, debe seguir el formato reconocido por Acciones de GitHub: Establecer un parámetro de salida.
Preparación de la aplicación .NET para Acciones de GitHub
GitHub Actions admiten dos modalidades de desarrollo de aplicaciones, ya sea
- JavaScript (opcionalmente TypeScript)
- Contenedor de Docker (cualquier aplicación que se ejecute en Docker)
El entorno virtual donde se hospeda la acción de GitHub puede o no tener instalado .NET. Para obtener información sobre lo que está preinstalado en el entorno de destino, consulte Entornos virtuales de Acciones de GitHub. Aunque es posible ejecutar comandos de la CLI de .NET desde los flujos de trabajo de Acciones de GitHub, para una Acción de GitHub basada en .NET más completa, se recomienda containerizar la aplicación. Para obtener más información, consulte Envasar en contenedor una aplicación .NET.
El archivo Dockerfile
Un Dockerfile es un conjunto de instrucciones para compilar una imagen. En el caso de las aplicaciones .NET, el Dockerfile normalmente se encuentra en la raíz del directorio junto a un archivo de solución.
# 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" ]
Nota:
La aplicación .NET de este tutorial se basa en el SDK de .NET como parte de su funcionalidad. El Dockerfile crea un nuevo conjunto de capas de Docker, independientemente de los anteriores. Comienza desde cero con la imagen del SDK y agrega la salida de compilación del conjunto anterior de capas. En el caso de las aplicaciones que no requieren el SDK de .NET como parte de su funcionalidad, deben basarse solo en el entorno de ejecución de .NET en su lugar. Esto reduce considerablemente el tamaño de la imagen.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Advertencia
Preste mucha atención a cada paso dentro del Dockerfile, ya que difiere del dockerfile estándar creado a partir de la funcionalidad "agregar compatibilidad con Docker". En concreto, los últimos pasos varían porque no se especifica un nuevo WORKDIR, lo que cambiaría la ruta de acceso a la aplicación ENTRYPOINT.
Los pasos anteriores de Dockerfile incluyen:
- Establecer la imagen base de
mcr.microsoft.com/dotnet/sdk:7.0como aliasbuild-env. - Copia del contenido y publicación de la aplicación .NET:
- La aplicación se publica mediante el
dotnet publishcomando .
- La aplicación se publica mediante el
- Aplicar etiquetas al contenedor.
- Retransmisión de la imagen del SDK de .NET desde
mcr.microsoft.com/dotnet/sdk:7.0 - Copia de la salida de compilación publicada desde
build-env. - Definir el punto de entrada, delegando en
dotnet /DotNet.GitHubAction.dll.
Sugerencia
McR en mcr.microsoft.com significa "Microsoft Container Registry" y es el catálogo de contenedores sindicado de Microsoft del centro oficial de Docker. Para obtener más información, consulte Catálogo de contenedores de sindicatos de Microsoft.
Precaución
Si usa un archivo global.json para anclar la versión del SDK, debe hacer referencia explícitamente a esa versión en el Dockerfile. Por ejemplo, si ha usado global.json para anclar la versión 5.0.300del SDK, el Dockerfile debe usar mcr.microsoft.com/dotnet/sdk:5.0.300. Esto impide interrumpir las acciones de GitHub cuando se publica una nueva revisión secundaria.
Definición de entradas y salidas de acción
En la sección Explorar la aplicación, aprendiste sobre la clase ActionInputs. Este objeto representa las entradas de la acción de GitHub. Para que GitHub reconozca que el repositorio es una acción de GitHub, debe tener un archivo action.yml en la raíz del repositorio.
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 }}
El archivo action.yml anterior define:
-
nameydescriptionen la acción de GitHub -
branding, que se usa en el Marketplace de GitHub para ayudar a identificar de manera más exclusiva tu acción. -
inputs, que se mapea uno a uno con la claseActionInputs - El
outputs, que se escribe en elProgramy se usa como parte de la composición del flujo de trabajo - El
runsnodo, que indica a GitHub que la aplicación es unadockeraplicación y qué argumentos se van a pasar a ella.
Para obtener más información, consulte Sintaxis de metadatos para Acciones de GitHub.
Variables de entorno predefinidas
Con Acciones de GitHub, obtendrá una gran cantidad de variables de entorno de forma predeterminada. Por ejemplo, la variable GITHUB_REF siempre contendrá una referencia a la rama o etiqueta que desencadenó la ejecución del flujo de trabajo.
GITHUB_REPOSITORY tiene el nombre del propietario y del repositorio, por ejemplo, dotnet/docs.
Debe explorar las variables de entorno predefinidas y usarlas en consecuencia.
Composición del flujo de trabajo
Con la aplicación .NET contenizada y las entradas y salidas de la acción definidas, ya estás listo para invocar la acción. No es necesario publicar acciones de GitHub en Marketplace de GitHub para su uso. Los flujos de trabajo se definen en el directorio .github/workflows de un repositorio como archivos 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.'
Importante
Para acciones de GitHub en contenedor, debe usar runs-on: ubuntu-latest. Para obtener más información, vea Sintaxis de flujo de trabajo jobs.<job_id>.runs-on.
El archivo YAML de flujo de trabajo anterior define tres nodos principales:
-
namedel flujo de trabajo. Este nombre también es lo que se usa al crear un distintivo de estado de flujo de trabajo. - El
onnodo define cuándo y cómo se desencadena la acción. - El
jobsnodo describe los distintos trabajos y pasos dentro de cada trabajo. Los pasos individuales consumen Acciones de GitHub.
Para obtener más información, consulte Creación del primer flujo de trabajo.
Al observar el steps nodo; la composición se vuelve más obvia.
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 representa la composición del flujo de trabajo. Los pasos se orquestan de forma que sean secuenciales, comunicativos y componibles. Con varias acciones de GitHub que representan los pasos, cada una de las cuales tiene entradas y salidas, se pueden crear flujos de trabajo.
En los pasos anteriores, puede observar:
El repositorio está desprotegido.
Cuando se ejecuta manualmente, se imprime un mensaje en el registro de flujo de trabajo.
Un paso identificado como
dotnet-code-metrics:-
uses: dotnet/samples/github-actions/DotNet.GitHubAction@maines la ubicación de la aplicación .NET en contenedor en este tutorial. -
envcrea una variable"GREETING"de entorno , que se imprime en la ejecución de la aplicación. -
withespecifica cada una de las entradas de acción necesarias.
-
Un paso condicional, denominado
Create pull requestse ejecuta cuando eldotnet-code-metricspaso especifica un parámetro de salida deupdated-metricscon un valor detrue.
Importante
GitHub permite la creación de secretos cifrados. Los secretos se pueden usar dentro de la composición del flujo de trabajo usando la sintaxis ${{ secrets.SECRET_NAME }}. En el contexto de una acción de GitHub, hay un token de GitHub que se rellena automáticamente de forma predeterminada: ${{ secrets.GITHUB_TOKEN }}. Para obtener más información, consulte Sintaxis de contexto y expresión para Acciones de GitHub.
Ponlo todo junto
El repositorio dotnet/samples de GitHub es el hogar de muchos proyectos de código fuente de ejemplo de .NET, incluida la aplicación de este tutorial.
El archivo CODE_METRICS.md generado es navegable. Este archivo representa la jerarquía de los proyectos que ha analizado. Cada proyecto tiene una sección de nivel superior y un emoji que representa el estado general de la complejidad ciclomática más alta para los objetos anidados. A medida que navega por el archivo, cada sección expone las oportunidades de exploración en profundidad con un resumen de cada área. El markdown tiene secciones plegables como una comodidad adicional.
La jerarquía avanza de:
- Archivo de proyecto para ensamblaje
- Ensamblado al espacio de nombres
- De espacio de nombres a tipo con nombre
- Cada tipo con nombre tiene una tabla y cada tabla tiene:
- Enlaces a números de línea para campos, métodos y propiedades
- Clasificaciones individuales para métricas de código
En acción
El flujo de trabajo especifica que on hacia push la main rama, se desencadena la acción para que se ejecute. Cuando se ejecuta, la pestaña Acciones de GitHub notificará el flujo de registro en directo de su ejecución. Este es un registro de ejemplo de la .NET code metrics ejecución:
Mejoras de rendimiento
Si ha seguido el ejemplo, es posible que haya observado que cada vez que se usa esta acción, realizará una compilación de Docker para esa imagen. Por lo tanto, cada disparador requiere cierto tiempo para construir el contenedor antes de ponerlo en marcha. Antes de publicar las acciones de GitHub en Marketplace, debe hacer lo siguiente:
- (automáticamente) Compilación de la imagen de Docker
- Inserción de la imagen de Docker en GitHub Container Registry (o en cualquier otro registro de contenedor público)
- Cambie la acción para no compilar la imagen, sino para usar una imagen de un registro público.
# 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!!
Para obtener más información, consulte Documentos de GitHub: Trabajar con el registro de contenedor.
Consulte también
- Host genérico de .NET
- Inserción de dependencias en .NET
- Valores de métricas de código
- Acción de GitHub de código abierto compilada en .NET con un flujo de trabajo para compilar y subir automáticamente la imagen de Docker.