Do Zero à Produção - Gere tudo com um único botão

Imagem de capa Do Zero à Produção - Gere tudo com um único botão

Do Zero à Produção - Gere tudo com um único botão

Como engenheiros de software, tentamos automatizar o máximo possível. Essa é a razão pela qual construímos nossa plataforma de integração contínua e implantação contínua. Mostrarei como você pode utilizar GitHub Actions, GitHub Pages, DocFx e, claro, .NET para criar seu próprio pipeline que, com um clique, faz o seguinte:

  • Execute testes e construa seu aplicativo
  • Solte o aplicativo, por exemplo, para nuget
  • Criar uma versão no GitHub com notas de versão
  • Atualize a documentação utilizando GitHub Pages e DocFx

Então vamos passo a passo. No final vou postar o link para o repositório se você quiser apenas usar isso. Você verá como configurar o repositório, como gerar a documentação automaticamente e como podemos liberar tudo com um único botão sob demanda.

Crie o Repositório

O primeiro passo não é novo para a maioria de nós. De qualquer forma, isso tem que ser feito, então vamos para o GitHub e criar um novo repositório.

Eu usei o .gitignore padrão mais o README.md padrão. Como licença eu escolho o MIT como sempre escolho isso. Todos os meus repositórios públicos são gratuitos para uso em projetos privados e empresariais. Mas sinta-se à vontade para usar a licença que melhor lhe convier. Agora apenas clonamos o repositório para nosso PC local. Eu sou um usuário pesado do git cli, portanto, você verá muitos exemplos de linha de comando do git em vez de GUI. Por favor, pegue a ferramenta com a qual você se sente mais confortável.

git clone https://github.com/linkdotnet/deployment-template.git e lá você tem seu novo repositório.

A estrutura de pastas

Agora antes de começar a encher o repositório com live eu quero mostrar a estrutura de pastas que vou usar ao longo deste post. Sinta-se à vontade para adaptá-lo às suas necessidades. Primeiro vou criar 4 novas pastas no diretório raiz

  • .github onde as ações do GitHub ficarão
  • docs onde nossa documentação ficará (feita por DocFx)
  • src onde nosso código de produção ficará
  • testes onde nosso código de teste ficará

Nossa biblioteca e projeto de teste

Agora é hora de criar uma nova solução, incluindo um projeto de biblioteca e um projeto de teste. Vou utilizar a linha de comando para isso. Sinta-se à vontade para usar seu IDE favorito.

No diretório raiz, usarei os seguintes comandos:

  • dotnet new sln - Cria nossa solução. Se você não fornecer nenhum argumento, a solução terá o mesmo nome do diretório em que foi criada. Com -n você pode passar o nome da solução assim: dotnew new sln -n MyFancySolution .
  • dotnet new classlib --output src/MyLibrary irá gerar nossa biblioteca de classes chamada "MyLibrary" no diretório src.
  • dotnet sln add src/MyLibrary/ adicionará o projeto recém-adicionado à nossa solução.
  • dotnet new xunit --output tests/MyLibraryTests criará nosso projeto de teste xUnit em tests/MyLibraryTests. Você também pode usar o NUnit ou qualquer outro framework de teste.
  • dotnet sln add tests/MyLibraryTests adicionará o projeto de teste à nossa solução.

Primeiro passo feito! Você pode confirmar seu trabalho e enviá-lo para o GitHub.

Opcional - StyleCop e SonarAnalyzers

Esta parte é opcional e você não precisa usá-la, mas eu uso os dois pacotes em quase todos os projetos que uso. Os analisadores StyleCop, bem como os analisadores SonarAnalyters.CSharp, verificam seu código e avisam se houver algum problema em potencial. Mas, além disso, eles também podem impor alguns estilos de codificação, o que é muito bom. Outra grande vantagem é que você pode configurá-los para gerar um erro se uma API pública não estiver documentada. Eu recomendo fortemente que eles os usem, veremos mais tarde por que o último ponto é muito importante.

Esses dois pacotes são um pouco especiais para mim: eu os quero literalmente em todos os projetos, até os adiciono aos meus projetos de teste. Portanto, uso o arquivo Directory.Build.props para que todos os meus arquivos csproj instalem automaticamente esses dois pacotes.

Então eu vou adicionar esses arquivos. Se você quiser, vá para o repositório e copie e cole esses 3 arquivos:

  • Directory.Build.props
  • stylecop.analyzers.ruleset
  • stylecop.json

A predefinição que eu uso é uma boa mistura. Sinta-se à vontade para adotar as regras.

A biblioteca

Até agora nossa biblioteca está bem vazia e não temos testes. Não tão grande. Então vamos criar uma nova classe de exemplo que tem uma função pública. Vou usar a seguinte função:

namespace  MyLibrary;

///  <summary>
/// Can print "Hello World" to the user.
///  </summary>
public  class  HelloWorldPrinter
{
    /// <summary>
    /// Returns "Hello World" <paramref name="times"/> on the Console.
    ///  </summary>
    ///  <param name="times">How many times Hello World should be displayed.</param>
    ///  <returns>"Hello World" multiple times delimited by a newline.</returns>
    public  string  GetHellWorld(int times)
    {
        var hello = Enumerable.Range(0, times).Select(_ => new  string("Hello World"));
        return  string.Join(Environment.NewLine, hello);
    }
}

Como você vê, criei um novo HelloWorldPrinter que apenas retorna "Hello World" n vezes. Mas também importante temos uma documentação. Isso será muito importante em alguns passos. Eu o encorajaria a documentar seu código (API pública em sua biblioteca, não tudo!).

Adicione também um pequeno teste:

using MyLibrary;
using Xunit;

namespace  MyLibraryTests;

public  class  UnitTest1
{
     [Fact]
     public  void  ShouldReturnHelloWorldThreeTimes()
     {
         const  string expected = @"Hello World
Hello World
Hello World";
         var sut = new HelloWorldPrinter();

         var str = sut.GetHellWorld(3);

         Assert.Equal(expected, str);
      }
}

Vamos cometer isso e seguir em frente.

A primeira ação: verifique a compilação em commits

Agora temos nosso primeiro teste com a primeira lógica. De agora em diante, queremos construir e testar nosso aplicativo com cada commit. Para isso vamos criar uma ação no GitHub. Dentro da pasta .github, crie uma nova subpasta chamada workflows. E aí criamos nossa primeira ação: dotnet.yml. Você pode nomeá-lo como desejar. Não vou entrar muito em detalhes sobre como as ações funcionam. Você precisa de uma pequena compreensão de como eles funcionam, mas não se preocupe. Aqui e aqui para iniciantes pode ajudá-lo. Achei-os bastante auto-explicativos:

name:  .NET

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

  runs-on:  ubuntu-latest

  steps:
  -  uses:  actions/checkout@v2
  -  name:  Setup  .NET
     uses:  actions/setup-dotnet@v1
     with:
       dotnet-version:  6.0.x
       include-prerelease:  true
  -  name:  Restore  dependencies
     run:  dotnet  restore
  -  name:  Build
     run:  dotnet  build  --no-restore  -c  Release
  -  name:  Test
     run:  dotnet  test  -c  Release  --no-build

O que ele faz: em cada solicitação push para main ou pull que segmenta main, execute as seguintes etapas:

  • Faça check-out do nosso repositório para o GitHub Action Agent
  • Restaure todas as nossas dependências
  • Construir nosso aplicativo
  • E faça os testes

Vamos confirmar isso e ir para a guia Ações em seu repositório.

E toda vez que a compilação falhar, um dos analisadores vir um problema ou um de nossos testes falhar, você receberá uma indicação (e até um e-mail, se configurado). Você pode criar até mesmo um crachá de status que você pode colocar em seu README.md. Parece algo como isto: .NET.

CHANGELOG. md

Esta é a primeira parte da documentação. Quando lançamos, não queremos escrever notas de lançamento, queremos que elas sejam geradas automaticamente e colocadas no GitHub. Para isso usaremos um arquivo chamado CHANGELOG.md que colocaremos na raiz do repositório. Esse não é um arquivo especial você pode nomeá-lo como quiser, nós o tornamos especial depois ??.

A estrutura desse arquivo é por convenção. A ideia é do keepachangelog. Quando você adiciona um novo recurso ou uma correção, nós o colocamos na seção Não lançados. Assim que criamos uma versão, aproveitamos essas informações.

CHANGELOG.md

# Changelog

A nice description could be here.

<!-- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -->

## [Unreleased]

### Added
  - `HelloWorldPrinter` which creates the user
  
### Fixed
  - Made the world a bit better!

A ideia é simples, uma vez que lançamos, transformamos a parte não lançada na versão atual e a marcamos como tal. Portanto, o release deve ter o seguinte conteúdo:

### Added
  -  `HelloWorldPrinter` which creates the user
  
### Fixed
  - Made the world a bit better!

E nosso arquivo CHANGELOG.md deve ficar assim depois

## [Unreleased]

## [1.0.0] - 2022-04-10

### Added
  - `HelloWorldPrinter` which creates the user

### Fixed
  - Made the world a bit better!

Como isso é feito eu vou mostrar mais tarde.

DocFx - Documente nossa aplicação e API

DocFx é um gerador de documentos estáticos. No nosso caso, levará nossos projetos C# e algumas remarcações e criará um site. Condições perfeitas para a nossa página GitHub gratuita, onde podemos hospedar exatamente esses conteúdos.

Antes de criarmos qualquer documentação, precisamos instalar o DocFx. Existem várias maneiras. Eu gosto de instalá-lo via chocolate, mas existem opções diferentes. Vou apenas vincular o guia de início rápido se você precisar de uma configuração diferente. Com chocolatey, que é um gerenciador de pacotes para Windows, você pode simplesmente instalá-lo através do powershell com direitos elevados: choco install docfx. Reinicie seu console e você poderá inserir o seguinte comando em seu shell: docfx --version e deverá obter a versão atual do DocFx. Uma vez que temos isso, podemos configurar nosso projeto.

Na pasta doc podemos criar docfx com o seguinte comando:

docfx init -q -o site --apiSourceFolder ../../src/
  • docfx init criará o projeto.
  • -q significa bastante, então não temos muita saída do console, você pode omitir este parâmetro se desejar.
  • -o site esta será a saída. Nesse caso, isso significaria que todo o conteúdo do documento está em docs/site. -_ --apiSourceFolder ../../src/_ damos ao docfx uma dica de onde está nosso src. Isso é relativo ao arquivo docfx.json recém-criado que fica em docs/site.

Agora você pode entrar na nova pasta do site e fazer uma pequena alteração no docfx.json. Altere a primeira parte de "arquivos" de "src/.csproj" para apenas ".csproj". A razão é simples, você pode, em teoria, hospedar todo o seu código dentro da pasta src que foi criada pelo docfx. Eu pessoalmente não sou um grande fã disso, mas isso é com você.

{
  "metadata":  [
    {
      "src":  [
        {
          "files":  [
            "**.csproj"
          ],
          "src":  "../../src/"
         }
        ],
        

Agora estamos prontos e podemos dar uma olhada em nossa documentação. Para isso, siga novamente um diretório até docs e execute o seguinte comando: docfx site/docfx.json. Isso compilará todos os arquivos e gerará a documentação da API. E depois chame _docfx serve site/site. Isso servirá a página da Web em localhost:8080. Deve ser algo assim.

Na parte superior você também tem a Documentação da API que deve ser criada automaticamente. Agora, espero que tenha feito sentido para você aplicarmos a documentação pública da API. Por conveniência, eu adicionaria um script cmd ou bash que faz esses dois comandos juntos na pasta docs:

docfx site/docfx.json
docfx serve site/_site

Não entrarei em detalhes sobre como escrever a documentação, mas fornecerei este link onde você poderá obter mais informações. Agora é o momento perfeito para comprometer nosso trabalho e continuar a jornada. Faça uma pausa se desejar ??.

Criar a página do GitHub

Agora que temos a documentação em vigor, podemos pensar onde e como podemos implantar isso. Na melhor das hipóteses, podemos fazer isso sob demanda ou sempre que tivermos um lançamento. Nós não necessariamente queremos isso em cada commit porque então mostraríamos ao usuário informações novas ou alteradas que não fazem sentido para ele neste momento.

Para começar, queremos criar 2 novas ramificações. Um chamamos de estável e outro chamamos de páginas gh. Este último é bastante óbvio. Usamos esse branch para servir nossa documentação. estável será nosso espelho da versão mais recente ou da versão mais recente com documentação atualizada. Agora, por que temos dois ramos? Um principal e um estável. Imagine que você só quer alterar sua documentação sem enviar conteúdo real ou liberar algo, isso seria difícil com um branch. Portanto, temos estável. Se queremos apenas atualizar a documentação, mesclamos as alterações em estável (stable) e principal (main) (também podemos automatizar isso).

Depois de criarmos nossas ramificações, vamos para a página de configurações da seção GitHub páginas. Lá selecione como fonte (Source) o branch gh-páginas. Você notará que imediatamente uma ação é iniciada.

Depois, você pode clicar no link oferecido a você acima da Fonte que você alterou alguns segundos atrás. Agora isso não parece tão ruim, mas não é o conteúdo que queremos ter.

A parte que falta é a seguinte: Toda vez que alguém ou alguma coisa empurra para estável nós queremos recompilar nossa documentação. E fazemos o mesmo que fizemos localmente anteriormente. Então, vamos para a pasta github/workflow e adicione um novo arquivo yml. Vou chamá-lo de docs.yml. A ação irá:

  • Confira nosso repositório
  • Baixar docx
  • Executar docfx
  • Publique as alterações no branch gh-pages
name:  Docs

on:
  push:
    branches:
    -  stable
  workflow_dispatch:

jobs:
  generate-docs:

    runs-on:  windows-latest

    steps:
    -  uses:  actions/checkout@v2
    
    -  name:  Setup  .NET  6.0
       uses:  actions/setup-dotnet@v1
       with:
         dotnet-version:  6.0.x

     -  name:  Setup  DocFX
        uses:  crazy-max/ghaction-chocolatey@v1
        with:
          args:  install  docfx

      -  name:  DocFX  Build
         working-directory:  docs
         run:  docfx  site\docfx.json
         continue-on-error:  false

      -  name:  Publish
         if:  github.event_name  ==  'push'
         uses:  peaceiris/actions-gh-pages@v3
         with:
           github_token:  ${{  secrets.GITHUB_TOKEN  }}
           publish_dir:  docs/site/_site
           force_orphan:  true

Agora em relação aos segredos.GITHUB_TOKEN: Isso é predefinido e você não precisa definir isso. Vamos aprender/usar segredos "reais" daqui a pouco. Certifique-se de que tudo o que você faz está no branch principal. Mal vamos interagir com o branch estável. Ao enviar essas alterações, você pode acessar as ações do Github e selecionar nosso fluxo de trabalho do Documentos recém-definido por meio do fluxo de trabalho Executar. Depois de um tempo, você pode verificar novamente sua página do GitHub e pronto, você verá uma página familiar. O do DocFx e nossa API.

Viva, nós fizemos isso. Nós chegamos muito longe agora. Muitos processos são automatizados, mas podemos fazer mais!

Mesclar automaticamente o estável para o principal

O próximo fluxo de trabalho fará o backmerge automaticamente de quaisquer alterações de estável para principal. Se a mesclagem não for bem-sucedida, criaremos um novo problema no mesmo repositório para que o autor esteja ciente do problema e tenha que resolver o conflito manualmente.

Crie um novo arquivo de fluxo de trabalho (em .github/workflows) chamado backmerge.yml com o seguinte conteúdo:

name:  Back  merge  to  main
on:
  push:
    branches:
      -  stable
    workflow_dispatch:

jobs:
  back-merge:
    if:  github.event.pull_request.merged  ==  true
    timeout-minutes:  2
    runs-on:  ubuntu-latest
    steps:
    -  uses:  actions/checkout@v2
    -  name:  Set  Git  config
       run:  |
             git config --local user.email "actions@github.com"
             git config --local user.name "Github Actions"
     -  name:  Merge  master  back  to  dev
        run:  |
             git fetch --unshallow
             git checkout main
             git pull
             git merge --no-ff origin/stable -m "Auto-merge stable back to main"
             git push

     -  name:  Create  issue  if  failed
        if:  failure()
        uses:  JasonEtco/create-an-issue@v2
        env:
          GITHUB_TOKEN:  ${{  secrets.GITHUB_TOKEN  }}
        with:
          filename:  .github/backmerge-failed.md
          update_existing:  true

A última parte é um pouco interessante. Se falharmos, criamos um novo problema. Como template eu defini um arquivo em .github/backmerge-failed.md. Este será o conteúdo do problema criado pela ação do GitHub. Eu uso um conteúdo muito simples como este:

---
title:  Back  merge  from  stable  to  main  failed
labels:  input  needed
---

The  back  merge  from  stable  to  main  failed.  Please  investigate  the  GitHub  Action  and  resolve  the  conflict  manually.

O título será usado como título da edição e também poderá atribuir etiquetas automaticamente.

Defina algumas informações básicas do NuGet em nosso csproj

Estamos tão perto. Fique comigo, estamos quase na linha de chegada. A próxima coisa importante é que precisamos de um ID exclusivo para nossa biblioteca ou aplicativo. Você pode fazer isso via Visual Studio ou editar diretamente seu csproj. Eu uso JetBrains Rider para isso:

Agora defina a versão para algo muito baixo como 0.0.1 e construa o projeto. Certifique-se também de gerar um pacote nuget pelo menos uma vez. Precisamos disso mais tarde para gerar a chave de API do NuGet. Depois de construir o projeto e ter um pacote nuget, remova a seguinte linha novamente < GeneratePackageOnBuild>true</ GeneratePackageOnBuild>. Caso contrário, isso pode levar a problemas no final.

Obtendo alguns segredos!

Agora precisamos de 2 segredos para adicionar ao nosso repositório: Nossa API NuGet para que possamos publicar pacotes e um Token de Acesso Pessoal.

NuGet

Vá até o Nuget para criar uma nova chave.

Como não temos um pacote agora, temos que usar o upload de nosso pacote criado anteriormente com uma versão muito baixa. Não se preocupe, podemos deslistá-lo diretamente depois, mas as chaves de API têm como escopo os pacotes. Portanto, se seu pacote não for conhecido, não podemos criar nele. Portanto, basta carregar o pacote nuget criado anteriormente.

Após carregarmos o pacote, podemos gerar diretamente o token:

Depois vá ao seu repositório e adicione a chave NuGet como segredo:

Token de acesso pessoal

Expliquei anteriormente que você já tem algum tipo de token de acesso chamado secrets.GITHUB_TOKEN que podemos usar para ações, mas eles têm um grande problema: imagine que você tem uma ação que envia algumas alterações para nosso branch estável. Não seria bom que nossa ação doc pegasse isso e enviasse as alterações para o branch gh-pages? Bem, isso só funciona com um PAT curto do Personal Access Token. Para isso, você deve acessar o site do Token e gerar um novo token. Apenas a parte do repo deve ser marcada para a ação que escreveremos.

Copie o Token conforme mostrado abaixo:

E por último crie um novo segredo em nosso repositório com o conteúdo do nosso PAT. Portanto, devemos ter 2 segredos em nosso repositório.

Criar uma versão

A última parte chegou. Reunimos todas as informações e criamos um release. Agora a ideia é simples, temos duas entradas: A nova versão que o usuário deve fornecer. Algo como 1.0.0 ou 1.2.0-beta.2. Usaremos esta versão nos seguintes locais:

  • Alteramos *Unreleased em nosso CHANGELOG.md para a versão fornecida
  • O pacote NuGet terá esta versão
  • O lançamento terá esta versão
  • Vamos criar uma tag com essa versão

A outra entrada é se tivermos um pré-lançamento. Há apenas um lugar onde isso é importante: a página de lançamento do GitHub. O NuGet não se importa com esse sinalizador. O NuGet usa o controle de versão semântico para determinar se um pacote é um pré-lançamento ou não. Basicamente tudo que tem uma string após o terceiro dígito é considerado um pré-lançamento (1.0.0-rc.1).

Além disso, quando nosso fluxo de trabalho é concluído, ele aciona automaticamente as atualizações da documentação à medida que mesclamos as alterações para estável também. Perfeito! Estamos no controle total quando lançamos, mas uma vez que decidimos fazê-lo, tudo é totalmente automatizado.

name:  create-release

on:
  workflow_dispatch:
    inputs:
     versionIncrement:
      description:  'The new version. For example: 1.1.0'
      required:  true
      default:  ''
     prerelease:
       description:  'Is this a pre-release?'
       type:  boolean
       required:  false
       default:  false

jobs:
  release:
  name:  Publish  new  release
  runs-on:  ubuntu-latest
  steps:

  -  name:  Checkout  repository
     uses:  actions/checkout@v2
     with:
       token:  ${{  secrets.PAT  }}
       persist-credentials:  true
       fetch-depth:  0

   -  name:  Setup  dotnet
      uses:  actions/setup-dotnet@v2
      with:
        dotnet-version:  6.0.x
   -  name:  Test
      run:  dotnet  test  -c  Release  --no-build

   -  name:  Get  changelog  entries
      id:  changelog
      uses:  mindsers/changelog-reader-action@v2
      with:
        version:  Unreleased
        path:  ./CHANGELOG.md

   -  name:  Update  CHANGELOG  file
      uses:  thomaseizinger/keep-a-changelog-new-release@1.2.1
      with:
        version:  ${{  github.event.inputs.versionIncrement  }}

   -  name:  Set  git  config
      run:  |
         git config --local user.email "linkdotnet@action.com"
         git config --local user.name "LinkDotNet Bot"

    -  name:  Commit  changes  and  push  changes
       run:  |
       git add CHANGELOG.md
       git commit -m "Update Changelog.md for ${{github.event.inputs.versionIncrement}} release" git push origin main
       git push origin main

    -  name:  Create  release  on  GitHub
       uses:  thomaseizinger/create-release@1.0.0
       env:
         GITHUB_TOKEN:  ${{  secrets.PAT  }}
       with:
         tag_name:  v${{  github.event.inputs.versionIncrement  }
         target_commitish:  ${{  env.RELEASE_COMMIT_HASH  }}
         name:  v${{  github.event.inputs.versionIncrement  }}
         body:  ${{  steps.changelog.outputs.changes  }}
         draft:  false
         prerelease:  ${{  github.event.inputs.prerelease  }}

    -  name:  Create  release  package
       run:  |
          dotnet pack -c RELEASE -p:PackageVersion=${{ github.event.inputs.versionIncrement }} -o ${GITHUB_WORKSPACE}/packages

    -  name:  Upload  to  nuget
       run:  |
          dotnet nuget push ${GITHUB_WORKSPACE}/packages/*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols

    -  name:  Merge  main  to  stable
       run:  |
          git fetch
          git checkout stable
          git pull
          git merge --no-ff -X theirs origin/main -m "Updating to newest release"

    -  name:  Push  changes
       uses:  ad-m/github-push-action@master
       with:
          github_token:  ${{  secrets.PAT  }}
          branch:  stable
          force:  true

Alguns detalhes aqui:

-  name:  Push  changes
      uses:  ad-m/github-push-action@master
      with:
         github_token:  ${{  secrets.PAT  }}
         branch:  stable
         force:  true

Estamos usando nosso PAT para que todas as ações, conforme explicado acima, sejam acionadas.

-  name:  Create  release  on  GitHub
   uses:  thomaseizinger/create-release@1.0.0
   env:
     GITHUB_TOKEN:  ${{  secrets.PAT  }}
   with:
      tag_name:  v${{  github.event.inputs.versionIncrement  }}
      target_commitish:  ${{  env.RELEASE_COMMIT_HASH  }}
      name:  v${{  github.event.inputs.versionIncrement  }}
      body:  ${{  steps.changelog.outputs.changes  }}
      draft:  false
      prerelease:  ${{  github.event.inputs.prerelease  }}

Isso criará o lançamento com a tag definida como nossa variável. Vamos adicionar v em frente a ele ficar com a "norma". Também podemos ver que usamos o conteúdo de CHANGELOG.md para preencher o corpo da nossa página de lançamento.

Resultado

Se você confirmar este arquivo e vá para o painel de ações.

Depois de executado com sucesso, podemos ver que temos um novo commit no main, além de uma nova entrada na seção de lançamento do nosso repositório:

Vamos dar uma olhada no nosso CHANGELOG.md:

# Changelog

A nice description could be here.

<!-- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -->

## [Unreleased]

## [1.0.0] - 2022-04-10

### Added

-  `HelloWorldPrinter` which creates the user

### Fixed

- Made the world a bit better!

[Unreleased]: https://github.com/linkdotnet/deployment-template/compare/1.0.0...HEAD

[1.0.0]: https://github.com/linkdotnet/deployment-template/compare/902a59583c17b4e0c437e156c038bd25ac2958f0...1.0.0

Também perfeito! A última coisa importante é que nosso pacote foi publicado no NuGet:

Isso é perfeito! Também podemos colocar uma marca de seleção por trás dessa tarefa. E é basicamente isso. Com apenas um clique de botão podemos liberar um pacote, criar as informações e tags no GitHub e atualizar nossa documentação também. Claro que você pode adotar esses fluxos de trabalho como desejar.

Recursos

O template GitHub Repository pode ser encontrado aqui