Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Saiba como criar um aplicativo .NET que pode ser usado como uma ação do GitHub. O GitHub Actions habilita a automação e a composição do fluxo de trabalho. Com o GitHub Actions, você pode criar, testar e implantar o código-fonte do GitHub. Além disso, as ações expõem a capacidade de interagir programaticamente com problemas, criar solicitações de pull, executar revisões de código e gerenciar branches. Para obter mais informações sobre a integração contínua com o GitHub Actions, consulte Compilar e testar o .NET.
Neste tutorial, você aprenderá como:
- Preparar um aplicativo .NET para o GitHub Actions
- Definir entradas e saídas de ação
- Compor um fluxo de trabalho
Pré-requisitos
- Uma conta do GitHub
- O SDK do .NET 6 ou posterior
- Um IDE (ambiente de desenvolvimento integrado) do .NET
- Fique à vontade para usar o IDE do Visual Studio
A intenção do aplicativo
O aplicativo neste tutorial executa a análise de métricas de código:
Analisando e descobrindo arquivos *.csproj e *.vbproj.
Analisando o código-fonte descoberto nesses projetos para:
- Complexidade ciclomática
- Índice de manutenção
- Profundidade da herança
- Acoplamento de classe
- Número de linhas do código-fonte
- Linhas aproximadas do código executável
Criando (ou atualizando) um arquivo CODE_METRICS.md .
O aplicativo não é responsável por criar uma solicitação de pull com as alterações no arquivo CODE_METRICS.md. Essas alterações são gerenciadas como parte da composição do fluxo de trabalho.
As referências ao código-fonte neste tutorial têm partes do aplicativo omitidas para fins de brevidade. O código completo do aplicativo está disponível no GitHub.
Explorar o aplicativo
O aplicativo de console do .NET usa o CommandLineParser pacote NuGet para analisar argumentos no 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]);
}
}
}
A classe de entradas de ação anterior define várias entradas necessárias para que o aplicativo seja executado com êxito. O construtor gravará o valor da "GREETINGS" variável de ambiente se um estiver disponível no ambiente de execução atual. As propriedades Name e Branch são analisadas e atribuídas a partir do último segmento de uma cadeia de caracteres delimitada por "/".
Com a classe de entradas de ação definida, concentre-se no arquivo 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);
}
O Program arquivo é simplificado para fins de brevidade, para explorar a fonte de exemplo completa, consulte Program.cs. Os mecanismos em vigor demonstram o código padrão necessário para usar:
Referências externas de projeto ou pacote podem ser usadas e registradas com injeção de dependência.
Get<TService> é uma função local estática, que requer a instância IHost e é usada para resolver serviços necessários. Com o CommandLine.Parser.Default Singleton, o aplicativo obtém uma instância parser do args. Quando os argumentos não podem ser analisados, o aplicativo sai com um código de saída diferente de zero. Para obter mais informações, consulte Configurando códigos de saída para ações.
Quando os args são analisados com êxito, o aplicativo foi chamado corretamente com as entradas necessárias. Nesse caso, uma chamada para a funcionalidade StartAnalysisAsync primária é feita.
Para gravar valores de saída, você deve seguir o formato reconhecido pelo GitHub Actions: Definindo um parâmetro de saída.
Preparar o aplicativo .NET para o GitHub Actions
O GitHub Actions dá suporte a duas variações de desenvolvimento de aplicativos,
- JavaScript (opcionalmente TypeScript)
- Contêiner do Docker (qualquer aplicativo executado no Docker)
O ambiente virtual em que o GitHub Action está hospedado pode ou não ter o .NET instalado. Para obter informações sobre o que é pré-instalado no ambiente de destino, consulte Ambientes Virtuais do GitHub Actions. Embora seja possível executar comandos da CLI do .NET nos fluxos de trabalho do GitHub Actions, para um funcionamento mais completo de uma GitHub Action baseada em .NET, recomendamos que você conteinerize o aplicativo. Para obter mais informações, consulte Containerize um aplicativo .NET.
O Dockerfile
Um Dockerfile é um conjunto de instruções para criar uma imagem. Para aplicativos .NET, o Dockerfile geralmente fica na raiz do diretório ao lado de um arquivo de solução.
# 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" ]
Observação
O aplicativo .NET neste tutorial depende do SDK do .NET como parte de sua funcionalidade. O Dockerfile cria um novo conjunto de camadas do Docker, independentemente das anteriores. Começa do zero com a imagem do SDK e incorpora a saída de compilação do conjunto anterior de camadas. Para aplicativos que não exigem o SDK do .NET como parte de sua funcionalidade, eles devem depender apenas do Runtime do .NET. Isso reduz muito o tamanho da imagem.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Aviso
Preste muita atenção a cada etapa dentro do Dockerfile, pois ele difere do Dockerfile padrão criado a partir da funcionalidade "adicionar suporte ao Docker". Em especial, as últimas etapas variam por não especificarem um novo WORKDIR, o que alteraria o caminho para o ENTRYPOINT do aplicativo.
As etapas anteriores do Dockerfile incluem:
- Definindo a imagem base de
mcr.microsoft.com/dotnet/sdk:7.0como o aliasbuild-env. - Copiando o conteúdo e publicando o aplicativo .NET:
- O aplicativo é publicado usando o
dotnet publishcomando.
- O aplicativo é publicado usando o
- Aplicando rótulos ao contêiner.
- Retransmissão da imagem do SDK do .NET
mcr.microsoft.com/dotnet/sdk:7.0 - Copiando o resultado de compilação publicado do
build-env. - Definindo o ponto de entrada, que delega a
dotnet /DotNet.GitHubAction.dll.
Dica
O MCR em mcr.microsoft.com significa "Microsoft Container Registry", e é o catálogo de contêineres sindicalizado da Microsoft do hub oficial do Docker. Para obter mais informações, consulte o catálogo de contêineres de sindicatos da Microsoft.
Cuidado
Se você usar um arquivo global.json para fixar a versão do SDK, deverá se referir explicitamente a essa versão em seu Dockerfile. Por exemplo, se você usou global.json para fixar a versão 5.0.300do SDK, o Dockerfile deverá usar mcr.microsoft.com/dotnet/sdk:5.0.300. Isso impede a interrupção do GitHub Actions quando uma nova revisão secundária é lançada.
Definir entradas e saídas de ação
Na seção Explorar o aplicativo , você aprendeu sobre a ActionInputs classe. Esse objeto representa as entradas para a Ação do GitHub. Para que o GitHub reconheça que o repositório é uma Ação do GitHub, você precisa ter um arquivo action.yml na raiz do repositório.
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 }}
O arquivo action.yml anterior define:
- A ação
nameedescriptiondo GitHub - O
branding, que é usado no GitHub Marketplace para ajudar a identificar sua ação de forma mais exclusiva - O
inputs, que mapeia um para um com a classeActionInputs - O
outputs, que é gravado noPrograme usado como parte da composição do fluxo de trabalho - O
runsnó, que informa ao GitHub que o aplicativo é umdockeraplicativo e quais argumentos fornecer a ele
Para obter mais informações, consulte a sintaxe de metadados para o GitHub Actions.
Variáveis de ambiente predefinidas
Com o GitHub Actions, você obterá muitas variáveis de ambiente por padrão. Por exemplo, a variável GITHUB_REF sempre conterá uma referência à ramificação ou marca que disparou a execução do fluxo de trabalho.
GITHUB_REPOSITORY tem o nome do proprietário e do repositório, por exemplo, dotnet/docs.
Você deve explorar as variáveis de ambiente predefinidas e usá-las adequadamente.
Composição de fluxo de trabalho
Com o aplicativo .NET conteinerizado e as entradas e saídas de ação definidas, você está pronto para utilizar a ação. O GitHub Actions não precisa ser publicado no GitHub Marketplace para ser usado. Os fluxos de trabalho são definidos no diretório .github/fluxos de trabalho de um repositório como arquivos 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 ações do GitHub em contêineres, você precisa usar runs-on: ubuntu-latest. Para obter mais informações, consulte a sintaxe jobs.<job_id>.runs-ondo fluxo de trabalho.
O arquivo YAML de fluxo de trabalho anterior define três nós primários:
- O
namedo fluxo de trabalho. Esse nome também é usado ao criar um selo de status de fluxo de trabalho. - O
onnó define quando e como a ação é disparada. - O
jobsnó delimita os vários trabalhos e etapas em cada trabalho. As etapas individuais utilizam o GitHub Actions.
Para obter mais informações, consulte Criando seu primeiro fluxo de trabalho.
Focando no steps nó, a composição é mais óbvia:
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.'
O jobs.steps representa a composição do fluxo de trabalho. As etapas são orquestradas de modo que sejam em sequência, comunicativas e componíveis. Com várias Ações do GitHub representando etapas, cada uma com entradas e saídas, os fluxos de trabalho podem ser compostos.
Nas etapas anteriores, você pode observar:
O repositório está verificado.
Uma mensagem é impressa no log de fluxo de trabalho, quando executada manualmente.
Uma etapa identificada como
dotnet-code-metrics:-
uses: dotnet/samples/github-actions/DotNet.GitHubAction@mainé o local do aplicativo .NET em contêineres neste tutorial. -
envcria uma variável"GREETING"de ambiente, que é impressa na execução do aplicativo. -
withespecifica cada uma das entradas de ação necessárias.
-
Uma etapa condicional, nomeada
Create pull request, é executada quando a etapadotnet-code-metricsespecifica um parâmetro de saídaupdated-metricscom um valor detrue.
Importante
O GitHub permite a criação de segredos criptografados. Os segredos podem ser usados na composição do fluxo de trabalho usando a sintaxe ${{ secrets.SECRET_NAME }}. No contexto de uma Ação do GitHub, há um token do GitHub que é preenchido automaticamente por padrão: ${{ secrets.GITHUB_TOKEN }}. Para obter mais informações, consulte a sintaxe de contexto e expressão do GitHub Actions.
Coloque tudo junto
O repositório GitHub dotnet/samples abriga muitos projetos de código-fonte de exemplo do .NET, incluindo o aplicativo neste tutorial.
O arquivo CODE_METRICS.md gerado é navegável. Esse arquivo representa a hierarquia dos projetos analisados. Cada projeto tem uma seção no nível mais alto e um emoji que representa o status geral do projeto em relação à complexidade ciclomática mais alta encontrada entre os objetos aninhados. À medida que você navega pelo arquivo, cada seção expõe oportunidades de detalhamento com um resumo de cada área. O markdown tem seções recolhiveis como uma conveniência adicional.
A hierarquia segue da seguinte forma:
- Arquivo de projeto para montagem
- Assembly para namespace
- Namespace para tipo nomeado
- Cada tipo nomeado tem uma tabela e cada tabela tem:
- Links para números de linha para campos, métodos e propriedades
- Classificações individuais para métricas de código
Em ação
O fluxo de trabalho especifica que on , push para o main branch, a ação é disparada para execução. Quando ele for executado, a guia Ações no GitHub relatará o fluxo de log ao vivo de sua execução. Aqui está um exemplo de log da execução .NET code metrics :
Aprimoramentos de desempenho
Se você seguiu o exemplo, talvez tenha notado que sempre que essa ação for usada, ela fará um build do Docker para essa imagem. Portanto, cada gatilho enfrenta algum tempo para compilar o contêiner antes de executá-lo. Antes de lançar o GitHub Actions no marketplace, você deve:
- (automaticamente) Criar a imagem do Docker
- Enviar a imagem do docker por push para o Registro de Contêiner do GitHub (ou qualquer outro registro de contêiner público)
- Altere a ação para não criar a imagem, mas para usar uma imagem de um 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 obter mais informações, consulte GitHub Docs: Trabalhando com o registro de contêiner.
Consulte também
- Host Genérico .NET
- Injeção de dependência no .NET
- Valores de métricas de código
- Ação GitHub de código aberto de compilação em .NET com um fluxo de trabalho para compilar e enviar a imagem Docker automaticamente.