Учебник. Создание действия GitHub Actions с помощью .NET
Узнайте, как создать приложение .NET, которое можно использовать в качестве действия GitHub. GitHub Actions обеспечивает автоматизацию и композицию рабочих процессов. С помощью GitHub Actions можно создавать, тестировать и развертывать исходный код из GitHub. Кроме того, действия предоставляют возможность программного взаимодействия с проблемами, создания запросов на вытягивание, выполнения проверок кода и управления ветвями. Дополнительные сведения о непрерывной интеграции с GitHub Actions см. в статье "Создание и тестирование .NET".
В этом руководстве описано следующее:
- Подготовка приложения .NET для GitHub Actions
- Определение входных и выходных данных действий
- Создание рабочего процесса
Необходимые компоненты
- Учетная запись GitHub
- Пакет SDK для .NET 6 или более поздней версии
- Интегрированная среда разработки .NET (IDE)
- Вы можете использовать интегрированную среду разработки Visual Studio
Намерение приложения
Приложение в этом руководстве выполняет анализ метрик кода следующим образом:
Сканирование и обнаружение файлов проекта *.csproj и *.vbproj .
Анализ обнаруженного исходного кода в этих проектах:
- Цикломатическая сложность
- Индекс удобства обслуживания
- Глубина наследования
- Взаимозависимость классов
- Количество строк исходного кода
- Приблизительные строки исполняемого кода
Создание (или обновление) файла CODE_METRICS.md .
Приложение не несет ответственности за создание запроса на вытягивание с изменениями в файле CODE_METRICS.md. Эти изменения управляются как часть композиции рабочего процесса.
Ссылки на исходный код в этом руководстве содержат части приложения, пропущенные для краткости. Полный код приложения доступен на сайте GitHub.
Ознакомиться с приложением
Консольное приложение .NET использует CommandLineParser
пакет NuGet для анализа аргументов в ActionInputs
объекте.
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]);
}
}
}
Предыдущий класс входных данных действия определяет несколько необходимых входных данных для успешного выполнения приложения. Конструктор записывает значение переменной "GREETINGS"
среды, если он доступен в текущей среде выполнения. Branch
Свойства Name
анализируются и назначаются из последнего сегмента "/"
строки с разделителями.
С помощью определенного класса входных данных действия сосредоточьтесь на файле 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);
}
Файл Program
упрощен для краткости, чтобы просмотреть полный пример источника, см. статью Program.cs. Механика на месте демонстрирует стандартный код, необходимый для использования:
Внешние ссылки на проект или пакет можно использовать и зарегистрировать с помощью внедрения зависимостей. Это Get<TService>
статическую локальную функцию, которая требует IHost
экземпляра и используется для разрешения необходимых служб. При использовании одноэлементного CommandLine.Parser.Default
приложения приложение получает parser
экземпляр из args
. Если аргументы не могут быть проанализированы, приложение завершает работу с кодом выхода без нуля. Дополнительные сведения см. в разделе "Настройка кодов выхода для действий".
При успешном анализе args приложение было вызвано правильно с необходимыми входными данными. В этом случае выполняется вызов основной функции StartAnalysisAsync
.
Чтобы записать выходные значения, необходимо выполнить формат, распознанный GitHub Actions: задание выходного параметра.
Подготовка приложения .NET для GitHub Actions
GitHub Actions поддерживает два варианта разработки приложений.
- JavaScript (необязательно TypeScript)
- Контейнер Docker (любое приложение, работающее в Docker)
Виртуальная среда, в которой размещено действие GitHub, может или не установлена .NET. Сведения о том, что предустановлено в целевой среде, см. в виртуальных средах GitHub Actions. Хотя можно запускать команды .NET CLI из рабочих процессов GitHub Actions, для более полного функционирования. GitHub Action на основе NET рекомендуется контейнеризировать приложение. Дополнительные сведения см. в разделе "Контейнеризация приложения .NET".
Файл Dockerfile
Dockerfile — это набор инструкций для создания образа. Для приложений .NET файл Dockerfile обычно находится в корне каталога рядом с файлом решения.
# 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" ]
Примечание.
Приложение .NET в этом руководстве использует пакет SDK для .NET в рамках его функциональных возможностей. Dockerfile создает новый набор слоев Docker, не зависящий от предыдущих. Он начинается с нуля с образа пакета SDK и добавляет выходные данные сборки из предыдущего набора слоев. Для приложений, которые не требуют пакета SDK для .NET в рамках их функциональных возможностей, они должны полагаться только на среду выполнения .NET. Это значительно сокращает размер изображения.
FROM mcr.microsoft.com/dotnet/runtime:7.0
Предупреждение
Обратите особое внимание на каждый шаг в Dockerfile, так как он отличается от стандартного Файла Dockerfile, созданного из функции "добавление поддержки Docker". В частности, последние несколько шагов различаются, не указывая новый WORKDIR
, который изменит путь к приложению ENTRYPOINT
.
Для Dockerfile предусмотрены следующие этапы.
- Установка базового образа из
mcr.microsoft.com/dotnet/sdk:7.0
в качестве псевдонимаbuild-env
. - Копирование содержимого и публикация приложения .NET:
- Приложение публикуется с помощью команды
dotnet publish
.
- Приложение публикуется с помощью команды
- Применение меток к контейнеру.
- Ретрансляция образа пакета SDK для .NET из
mcr.microsoft.com/dotnet/sdk:7.0
- Копирование опубликованных выходных данных сборки из .
build-env
- Определение точки входа, которая делегируется
dotnet /DotNet.GitHubAction.dll
.
Совет
MCR в mcr.microsoft.com
означает "Microsoft Container Registry", а также является каталогом для выобъединенного контейнера Майкрософт из официального центра Docker. Дополнительные сведения см. в каталоге контейнеров microsoft syndicates.
Внимание
Если вы используете файл global.json для закрепления версии пакета SDK, необходимо явно ссылаться на эту версию в Dockerfile. Например, если вы использовали global.json для закрепления версии 5.0.300
пакета SDK, следует использовать mcr.microsoft.com/dotnet/sdk:5.0.300
Dockerfile. Это предотвращает нарушение GitHub Actions при выпуске новой дополнительной редакции.
Определение входных и выходных данных действий
В разделе "Изучение приложения" вы узнали о ActionInputs
классе. Этот объект представляет входные данные для действия GitHub. Чтобы GitHub распознал, что репозиторий является действием GitHub, необходимо иметь файл action.yml в корне репозитория.
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 }}
Предыдущий файл action.yml определяет следующее:
description
Действиеname
GitHub- ,
branding
который используется в GitHub Marketplace для более уникальной идентификации вашего действия - Объект
inputs
, который сопоставляет один к одному с классомActionInputs
- Объект
outputs
, который записывается в состав рабочего процесса и используется вProgram
составе композиции рабочего процесса. - Узел
runs
, который сообщает GitHub, что приложение является приложениемdocker
и какие аргументы для передачи в него
Дополнительные сведения см. в синтаксисе метаданных для GitHub Actions.
Предопределенные переменные среды
С помощью GitHub Actions вы получите множество переменных среды по умолчанию. Например, переменная GITHUB_REF
всегда будет содержать ссылку на ветвь или тег, активировав выполнение рабочего процесса. GITHUB_REPOSITORY
имеет имя владельца и репозитория, например dotnet/docs
.
Необходимо изучить предварительно определенные переменные среды и использовать их соответствующим образом.
Состав рабочего процесса
С помощью контейнера приложения .NET и определенных входных и выходных данных действия можно использовать действие. Действия GitHub не требуются для публикации в GitHub Marketplace. Рабочие процессы определяются в каталоге github/workflows репозитория в виде файлов 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.'
Важно!
Для контейнерных действий GitHub необходимо использовать runs-on: ubuntu-latest
. Дополнительные сведения см. в синтаксисе jobs.<job_id>.runs-on
рабочего процесса.
Предыдущий файл YAML рабочего процесса определяет три основных узла:
- Тип
name
рабочего процесса. Это имя также используется при создании индикатора состояния рабочего процесса. - Узел
on
определяет, когда и как активируется действие. - Узел
jobs
описывает различные задания и шаги в каждом задании. Отдельные шаги используют GitHub Actions.
Дополнительные сведения см. в статье "Создание первого рабочего процесса".
Фокусируясь на steps
узле, композиция более очевидна:
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
состав рабочего процесса. Шаги оркестрируются таким образом, чтобы они были последовательными, коммуникатными и компонуемыми. С помощью различных действий GitHub Actions, представляющих шаги, каждый из которых имеет входные и выходные данные, рабочие процессы можно создавать.
На предыдущих шагах можно наблюдать:
Репозиторий проверка отключен.
Сообщение выводится в журнал рабочего процесса при выполнении вручную.
Шаг, определенный как
dotnet-code-metrics
:uses: dotnet/samples/github-actions/DotNet.GitHubAction@main
— это расположение контейнерного приложения .NET в этом руководстве.env
создает переменную"GREETING"
среды, которая печатается в выполнении приложения.with
указывает каждый из обязательных входных данных действия.
Условный шаг с именем
Create pull request
выполняется приdotnet-code-metrics
указании выходного параметраupdated-metrics
со значениемtrue
.
Важно!
GitHub позволяет создавать зашифрованные секреты. Секреты можно использовать в составе рабочего процесса с помощью синтаксиса ${{ secrets.SECRET_NAME }}
. В контексте действия GitHub существует маркер GitHub, который автоматически заполняется по умолчанию: ${{ secrets.GITHUB_TOKEN }}
Дополнительные сведения см. в синтаксисе контекста и выражений для GitHub Actions.
Сборка
Репозиторий dotnet/samples GitHub является домом для многих проектов исходного кода .NET, включая приложение в этом руководстве.
Созданный файл CODE_METRICS.md доступен для навигации. Этот файл представляет иерархию проанализированных проектов. Каждый проект имеет раздел верхнего уровня и эмодзи, представляющий общее состояние максимальной сложности цикломатики для вложенных объектов. При переходе по файлу каждый раздел предоставляет возможности детализации с сводкой по каждой области. Markdown содержит сворачиваемые разделы в качестве дополнительного удобства.
Иерархия выполняется из:
- Файл проекта для сборки
- Сборка для пространства имен
- Пространство имен для именованного типа
- Каждый именованный тип имеет таблицу, и каждая таблица имеет:
- Ссылки на номера строк для полей, методов и свойств
- Отдельные оценки для метрик кода
В действии
Рабочий процесс указывает, что on
push
ветвь main
активируется действие для выполнения. При выполнении вкладка "Действия " в GitHub сообщает поток динамического журнала его выполнения. Ниже приведен пример журнала из .NET code metrics
запуска:
Улучшения производительности
Если вы последовали по образцу, возможно, вы заметили, что при каждом использовании этого действия будет выполняться сборка docker для этого образа. Таким образом, каждый триггер сталкивается с некоторое время, чтобы создать контейнер перед его запуском. Перед выпуском GitHub Actions в Marketplace необходимо:
- (автоматически) Создание образа Docker
- Отправьте образ Docker в реестр контейнеров GitHub (или любой другой общедоступный реестр контейнеров)
- Измените действие, чтобы не создать образ, а использовать образ из общедоступного реестра.
# 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!!
Дополнительные сведения см . в документации GitHub: работа с реестром контейнеров.
См. также
- Универсальный узел .NET
- Внедрение зависимостей в .NET
- Значения метрик кода
- Сборка GitHub Action с открытым исходным кодом в .NET с рабочим процессом для создания и автоматического отправки образа Docker.