Bagikan melalui


Tutorial: Buat Tindakan GitHub dengan .NET

Pelajari cara membuat aplikasi .NET yang bisa digunakan sebagai Tindakan GitHub. Tindakan GitHub memungkinkan otomatisasi serta komposisi alur kerja. Dengan GitHub Actions, Anda bisa membangun, menguji, serta menyebarkan kode Anda dari GitHub. Selain itu, tindakan mengekspos kemampuan untuk berinteraksi secara terprogram dengan masalah, membuat permintaan pull, melakukan tinjauan kode, serta mengelola cabang. Untuk informasi selengkapnya mengenai integrasi berkelanjutan dengan GitHub Actions, lihat Membangun dan menguji .NET.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Menyiapkan aplikasi .NET untuk Tindakan GitHub
  • Menentukan input serta output tindakan
  • Membuat alur kerja

Prasyarat

Niat dari aplikasi

Aplikasi dalam tutorial ini melakukan analisis metrik kode dengan:

  • Memindai dan menemukan file proyek *.csproj dan *.vbproj.

  • Menganalisis kode sumber yang ditemukan dalam proyek ini untuk:

    • Kompleksitas siklomatik
    • Indeks keberlanjutan
    • Kedalaman pewarisan
    • Pemasangan kelas
    • Jumlah baris kode sumber
    • Perkiraan baris kode yang dapat dieksekusi
  • Membuat (atau memperbarui) file CODE_METRICS.md.

Aplikasi ini tidak bertanggung jawab untuk membuat permintaan pull dengan perubahan pada file CODE_METRICS.md. Perubahan ini dikelola sebagai bagian dari komposisi alur kerja.

Referensi ke kode sumber dalam tutorial ini memiliki bagian aplikasi yang dihilangkan untuk keringkasan. Kode aplikasi lengkap tersedia di GitHub.

Menjelajahi aplikasi

Aplikasi konsol .NET menggunakan paket CommandLineParser NuGet untuk mengurai argumen ke dalam objekActionInputs.

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]);
        }
    }
}

Kelas input tindakan sebelumnya menentukan beberapa input yang diperlukan agar aplikasi berhasil dieksekusi. Konstruktor akan menulis nilai variabel lingkungan "GREETINGS", apabila tersedia di lingkungan eksekusi saat ini. Properti Name dan Branch diurai dan ditetapkan dari segmen terakhir dari string "/" yang dibatasi.

Dengan kelas input tindakan yang ditentukan, fokus pada file 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);
}

File Program disederhanakan agar mudah, untuk menjelajahi sumber sampel lengkap, lihat Program.cs. Mekanisme di tempat menunjukkan kode boilerplate yang dibutuhkan untuk menggunakan:

Referensi proyek atau paket eksternal dapat digunakan, serta terdaftar dengan injeksi dependensi. Get<TService> adalah fungsi lokal statik, yang memerlukan instansIHost, serta digunakan untuk menyelesaikan layanan yang diperlukan. Dengan singleton CommandLine.Parser.Default, aplikasi mendapatkan instans parser dari args. Ketika argumen tidak dapat diurai, aplikasi keluar dengan kode keluar bukan nol. Untuk informasi selengkapnya, lihat Mengatur kode keluar untuk tindakan.

Ketika argumen berhasil diurai, aplikasi dipanggil dengan benar dengan input yang dibutuhkan. Dalam hal ini, panggilan ke fungsionalitas utama StartAnalysisAsync dilakukan.

Untuk menulis nilai output, Anda harus mengikuti format yang dikenali oleh GitHub Actions: Mengatur parameter output.

Menyiapkan aplikasi .NET untuk Tindakan GitHub

Tindakan GitHub mendukung dua variasi pengembangan aplikasi, baik

  • JavaScript ( TypeScript opsional)
  • Kontainer Docker (aplikasi apa pun yang berjalan di Docker)

Lingkungan virtual tempat Tindakan GitHub dihosting mungkin menginstal atau mungkin tidak menginstal .NET. Untuk informasi mengenai apa yang telah diinstal sebelumnya di lingkungan target, lihat GitHub Actions Virtual Environments. Meskipun dimungkinkan untuk menjalankan perintah .NET CLI dari alur kerja GitHub Actions, untuk fungsi yang lebih sepenuhnya . GitHub Action berbasis NET, kami sarankan Anda membuat kontainer aplikasi. Untuk informasi selengkapnya, lihat Menampung aplikasi .NET.

Dockerfile

Dockerfile adalah serangkaian instruksi untuk membangun sebuah gambar. Untuk aplikasi .NET, Dockerfile biasanya berada di akar direktori di samping file solusi.

# 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" ]

Catatan

Aplikasi .NET dalam tutorial ini bergantung pada .NET SDK sebagai bagian dari fungsionalitasnya. Dockerfile membuat sekumpulan lapisan Docker baru, independen dari yang sebelumnya. Ini dimulai dari awal dengan gambar SDK, dan menambahkan output build dari set lapisan sebelumnya. Untuk aplikasi yang tidak memerlukan .NET SDK sebagai bagian dari fungsionalitasnya, mereka harus mengandalkan .NET Runtime saja sebagai gantinya. Ini sangat mengurangi ukuran dari gambar.

FROM mcr.microsoft.com/dotnet/runtime:7.0

Peringatan

Perhatikan dengan cermat setiap langkah dalam Dockerfile, karena berbeda dari Dockerfile standar yang dibuat dari fungsionalitas "tambahkan dukungan docker". Secara khusus, beberapa langkah terakhir bervariasi dengan tidak menentukan yang baru WORKDIR yang akan mengubah jalur ke aplikasi ENTRYPOINT.

Langkah-langkah Dockerfile sebelumnya meliputi:

  • Mengatur gambar dasar dari mcr.microsoft.com/dotnet/sdk:7.0 sebagai alias build-env.
  • Menyalin konten dan menerbitkan aplikasi .NET:
  • Menerapkan label ke kontainer.
  • Menyampaikan gambar .NET SDK dari mcr.microsoft.com/dotnet/sdk:7.0
  • Menyalin output build yang diterbitkan dari build-env.
  • Menentukan titik masuk, yang mendelegasikan ke dotnet /DotNet.GitHubAction.dll.

Tip

MCR dalam mcr.microsoft.com adalah singkatan dari "Microsoft Container Registry", dan merupakan katalog kontainer sindikasi Microsoft dari hub Docker resmi. Untuk informasi selengkapnya, lihat Katalog kontainer sindikat Microsoft.

Perhatian

Jika Anda menggunakan file global.json untuk menyematkan versi SDK, Anda harus secara eksplisit merujuk ke versi tersebut di Dockerfile Anda. Misalnya, apabila Anda telah menggunakan global.json untuk menyematkan versi 5.0.300SDK, Dockerfile Anda harus menggunakan mcr.microsoft.com/dotnet/sdk:5.0.300. Ini mencegah melanggar GitHub Actions ketika revisi minor baru dirilis.

Menentukan input serta output tindakan

Di bagian Jelajahi aplikasi, Anda mempelajari tentang kelas ActionInputs. Objek ini mewakili input untuk Tindakan GitHub. Agar GitHub mengenali bahwa repositori adalah Tindakan GitHub, Anda harus memiliki file action.yml di akar repositori.

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 }}

File action.yml sebelumnya mendefinisikan:

  • name dan description dari Tindakan GitHub
  • branding, yang digunakan di GitHub Marketplace untuk membantu mengidentifikasi tindakan Anda secara lebih unik
  • inputs, yang memetakan satu-ke-satu dengan kelas ActionInputs
  • outputs, yang ditulis dalam Program serta digunakan sebagai bagian dari komposisi Alur Kerja
  • Simpul runs, yang memberi tahu GitHub bahwa aplikasi adalah aplikasi docker dan argumen apa yang harus diteruskan ke dalamnya

Untuk informasi selengkapnya, lihat Sintaks metadata untuk Tindakan GitHub.

Variabel lingkungan yang telah ditentukan sebelumnya

Dengan GitHub Actions, Anda akan mendapatkan banyak variabel lingkungan secara default. Misalnya, variabel GITHUB_REF akan selalu berisi referensi ke cabang atau tag yang memicu eksekusi alur kerja. GITHUB_REPOSITORY memiliki nama pemilik dan repositori, misalnya, dotnet/docs.

Anda harus menjelajahi variabel lingkungan yang telah ditentukan sebelumnya dan menggunakan variabel lingkungan dengan sesuai.

Komposisi alur kerja

Dengan aplikasi .NET yang dikontainerisasi, serta input dan output tindakan yang ditentukan, Anda siap untuk menghabiskan tindakan. GitHub Actions tidak perlu dipublikasikan di GitHub Marketplace untuk digunakan. Alur kerja didefinisikan dalam direktori .github/workflows dari repositori sebagai file 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.'

Penting

Untuk Tindakan GitHub dalam kontainer, Anda harus menggunakan runs-on: ubuntu-latest. Untuk informasi selengkapnya, lihat Sintaks Ruang Kerjajobs.<job_id>.runs-on.

File YAML alur kerja sebelumnya mendefinisikan tiga simpul utama:

  • name dari alur kerja. Nama ini juga yang digunakan saat membuat lencana status alur kerja.
  • Simpul on menentukan kapan serta bagaimana tindakan dipicu.
  • Simpul jobs menguraikan berbagai pekerjaan serta langkah-langkah dalam setiap pekerjaan. Langkah-langkah individual mengonsumsi Tindakan GitHub.

Untuk informasi selengkapnya, lihat Membuat alur kerja pertama Anda.

Berfokus pada simpul steps, komposisinya lebih jelas:

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 mewakili komposisi alur kerja. Langkah-langkah diorkestrasi skemanya sehingga mereka berurutan, komunikatif, serta dapat disusun. Dengan berbagai Tindakan GitHub yang mewakili langkah-langkah, masing-masing memiliki input dan output, alur kerja dapat disusun.

Pada langkah sebelumnya, Anda bisa mengamati:

  1. Repositori diperiksa.

  2. Pesan dicetak ke log alur kerja, ketika dijalankan secara manual.

  3. Langkah yang diidentifikasi sebagai dotnet-code-metrics:

    • uses: dotnet/samples/github-actions/DotNet.GitHubAction@main adalah lokasi aplikasi .NET dalam kontainer dalam tutorial ini.
    • env membuat variabel lingkungan "GREETING", yang dicetak dalam eksekusi aplikasi.
    • with menentukan masing-masing input tindakan yang diperlukan.
  4. Langkah kondisional, bernama Create pull request berjalan saat langkah dotnet-code-metrics menentukan parameter output dari updated-metrics dengan nilai true.

Penting

GitHub memungkinkan pembuatan dari rahasia terenkripsi. Rahasia dapat digunakan dalam komposisi alur kerja, menggunakan sintaks ${{ secrets.SECRET_NAME }}. Dalam konteks Tindakan GitHub, ada token GitHub yang secara otomatis diisi secara default: ${{ secrets.GITHUB_TOKEN }}. Untuk informasi selengkapnya, lihat Konteks dan sintaks ekspresi untuk Tindakan GitHub.

Satukan semuanya

Repositori GitHub dotnet/samples adalah rumah untuk banyak proyek kode sumber sampel .NET, termasuk aplikasi dalam tutorial ini.

File CODE_METRICS.md yang dihasilkan dapat dinavigasi. File ini mewakili hierarki proyek yang dianalisisnya. Setiap proyek memiliki bagian tingkat atas, serta emoji yang mewakili status keseluruhan kompleksitas siklomatik tertinggi untuk objek berlapis. Saat Anda menavigasi file, setiap bagian memaparkan peluang penelusuran paling detail dengan ringkasan dari setiap area. Markdown mempunyai bagian yang dapat diciutkan sebagai kenyamanan tambahan.

Hierarki berlangsung dari:

  • Proyek file ke assembly
  • Assembly ke namespace layanan
  • Namespace layanan ke jenis-bernama
  • Setiap jenis bernama memiliki tabel, dan setiap tabel memiliki:
    • Tautan ke nomor baris untuk bidang, metode, serta properti
    • Peringkat individual untuk metrik kode

Sedang bertindak

Alur kerja menentukan bahwa on adalah push ke cabang main, tindakan dipicu untuk dijalankan. Saat berjalan, tab Tindakan di GitHub akan melaporkan aliran log langsung eksekusinya. Berikut adalah contoh log dari eksekusi .NET code metrics:

.NET code metrics - GitHub Actions log

Peningkatan performa

Jika Anda mengikuti contoh, Anda mungkin memperhatikan bahwa setiap kali tindakan ini digunakan, tindakan ini akan melakukan pembangunan docker untuk gambar tersebut. Jadi, setiap pemicu diberikan beberapa waktu untuk membangun kontainer sebelum menjalankannya. Sebelum merilis GitHub Actions Anda ke marketplace, Anda harus:

  1. (secara otomatis) Membangun gambar Docker
  2. Mendorong gambar docker ke GitHub Container Registry (atau registri kontainer publik lainnya)
  3. Ubah tindakan agar tidak membangun gambar, tetapi menggunakan gambar dari registri publik.
# 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!!

Untuk informasi selengkapnya, lihat Dokumen GitHub: Bekerja dengan registri Kontainer.

Baca juga

Langkah berikutnya