С публикацией Orleans.SyncWork у меня появилась возможность изучить действия GitHub — способ автоматизации рабочих процессов. Вот некоторые из моих первых впечатлений от действия (стона).

Автоматизированный рабочий процесс

Прежде всего, что такое рабочий процесс и что значит его автоматизировать? Что ж, дорогой потенциальный читатель, рабочий процесс — это не что иное, как набор шагов, предпринятых для выполнения задачи.

Из Википедии:

Рабочий процесс состоит из организованного и повторяемого шаблона действий, обеспечиваемого систематической организацией ресурсов в процессы, которые преобразуют материалы, предоставляют услуги или обрабатывают информацию. Его можно изобразить как последовательность операций, работу человека или группы, работу организации персонала или один или несколько простых или сложных механизмов.

Вы можете думать о рабочем процессе как о шагах, предпринимаемых для достижения «чего-то». Этим «чем-то» может быть любое количество вещей, связанных с любым количеством предметов. В контексте этого поста мы в основном будем рассматривать рабочие процессы, связанные с конвейером сборки и выпуска, также обычно называемые непрерывной интеграцией (CI) и непрерывной доставкой (CD).

Я хотел бы охватить аспекты CI и CD в Orleans.SyncWork, так что давайте начнем.

Что вам (вероятно) понадобится

  • Опыт работы с CLI
  • Представление о шагах, которые необходимо предпринять для сборки, тестирования и развертывания кода. Если эти шаги уже у вас в голове в виде команд CLI, то вы уже прошли большую часть пути!
  • Немного терпения, чтобы правильно организовать рабочий процесс.

Непрерывная интеграция

Прежде чем вы сможете развертывать код через рабочий процесс (непрерывная доставка), вам необходимо иметь возможность безопасно интегрировать его в ваш main/trunk. Для dotnet с помощью нескольких команд CLI создание и тестирование кода выполняется довольно просто. Выполнение CI имеет дополнительное преимущество, заключающееся в том, что каждый раз создается совершенно новая среда для сборки, аналогичная идея, почему я так долго был сторонником сборки серверов.

Строить

dotnet build

Приведенная выше команда — это минимум, необходимый для создания файла решения или файла проекта на стороне dotnet. С точки зрения непрерывной интеграции вы можете добавить в команду несколько флажков, например:

dotnet build --configuration release

и тому подобное, посмотрите, что вам доступно в документации dotnet build.

Это может выглядеть так из CLI:

Тест

Дальше тестирование. Наверное, я уже говорил это слишком много раз, но протестируйте свой код! Особенно, если вы строите библиотеки! Тесты помогают убедиться, что код, который вы пишете, делает то, что вы говорите. Кроме того, тесты можно использовать в качестве документации таким образом, если тесты правильно названы и вызывают код аналогично тому, как его будет использовать ваш потребитель, они будут в лучшем месте, чтобы начать использовать то, что вы доставили.

Как и команда build, команда test также довольно проста:

dotnet test

Конечно, вышеприведенная команда является абсолютным минимумом, есть много параметров, которые также можно передать в dotnet test.

Тестовый запуск может выглядеть из CLI следующим образом:

Действие КИ

С приведенными выше командами dotnet build и dotnet test у нас есть большая часть того, что нам нужно для автоматического создания и тестирования нашего кода!

В документации есть много полезной информации, даже немного по тестированию .net. Я в значительной степени использовал документацию в качестве отправной точки, и в итоге это…

.github/рабочие процессы/ci.yml:

name: Build and test

on:
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
      with:
        fetch-depth: 0

    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x
    
    - name: Restore dependencies
      run: dotnet restore
    
    - name: Build
      run: dotnet build -c Release --no-restore
    
    - name: Test
      run: dotnet test -c Release --no-build --verbosity normal --filter "Category!=LongRunning"

Приведенный выше файл должен быть в основном простым, сначала мы даем имя нашему рабочему процессу с помощью name, указываем триггеры для рабочего процесса, в данном случае «по запросам на вытягивание против основной ветки». Затем файл определяет задание «сборка», в котором указывается ОС для запуска, а затем шаги. Шаги делают несколько важных вещей:

  • Проверьте код, поместив его в экземпляр ubuntu, который используется с предыдущего шага.
  • Настройте .NET с готовым действием
  • восстановить зависимости
  • построить файл решения в корне проекта
  • наконец проверить код

У нас есть несколько новых флагов в наших командах сборки и тестирования, а именно указание конфигурации выпуска и запрет на восстановление/сборку на шагах после того, как эти шаги уже были выполнены. И последнее замечание: --filter "Category!=LongRunning" — у меня возникли проблемы с выполнением тестов, которые я выложил. Локальный запуск занял 3 минуты, а на агенте сборки — более 25 минут. В связи с этим я добавил некоторые классификации «категорий» к более длительным тестам и исключил их из тестового прогона в приведенном выше файле ci.yml.

Непрерывная доставка

Непрерывная доставка очень похожа на непрерывную интеграцию и строится на ее основе. Я считаю, что CD должен делать все, что должен делать CI, или, что еще лучше, фактически полагаться на CI, а не переопределять шаги в вашем CICD, как это сделал я. Это было немного бессвязно, но CD должен делать все то же самое, что и CI, за исключением дополнительного шага фактической доставки (развертывания/отправки) кода как части его рабочего процесса.

Сложности доставки

Эта часть доставки может иметь много нюансов, которые значительно повышают сложность по сравнению с просто «CI». Что значит на самом деле доставить код? Ну, это может сильно зависеть от того, какой тип кода вы на самом деле предоставляете. В моем случае я доставляю пакет NuGet, в котором есть свои сложности, но что еще? Что ж, другая очевидная вещь, которая приходит на ум, — это веб-сайт/веб-API, который потенциально может иметь изменения базы данных для развертывания в дополнение к коду. Для меня это может быть намного сложнее, чем просто установка пакета NuGet. Как вы не только обрабатываете сбои, но и обнаруживаете их и выполняете откат в случае, если что-то пойдет не так с отправкой через веб-страницу или с отправкой базы данных? Возможно, однажды я смогу это изучить, а пока давайте вернемся к пакету NuGet.

Итак, есть ли сложности с доставкой пакета NuGet? Да. Управление версиями пакетов NuGet может оказаться сложной задачей, когда речь идет о ручном развертывании, а тем более о компакт-диске; поскольку существует требование неизменности пакетов NuGet. Означает ли это, что для каждой регистрации в каждой потенциальной ветке, которая будет отправлена ​​в NuGet, вам нужно обновить какой-то текстовый файл или код, чтобы указать следующую встроенную версию? Это было моей первоначальной мыслью, но, к счастью, это не так с помощью Nerdbank.GitVersioning.

Я не думаю, что мой CICD настроен точно так, как он у меня будет в конечном итоге, но сейчас он работает. Я установил инструмент и пакет NerdBank.GitVersioning, и теперь для каждой сборки я получаю уникальный номер версии для пакета NuGet при сборке. Я могу переключаться между предварительными и релизными пакетами и даже публиковать «ночные» сборки, которые содержат хэш фиксации, и все это от имени уникально идентифицируемых пакетов NuGet.

Было довольно много настроек, над которыми я все еще работаю, но эта статья уже достаточно длинная, взгляните на PR, если вам интересно: https://github. com/OrleansContrib/Orleans.SyncWork/pull/8 и https://github.com/OrleansContrib/Orleans.SyncWork/pull/13. TLDR заключается в том, что инструментарий nbgv использует историю git для изменения номера версии, используемого во время сборки, что позволяет использовать уникальные номера сборки каждый раз, когда запускается CI/CD.

компакт-диск действие

До сих пор было достаточно информации, но между нашим действием CI и информацией о GitVersioning у нас есть все, что нам нужно, чтобы собрать «первый проход» cicd.yml. Для CI мы делали сборки/тесты против PR для main. Для компакт-диска мы хотим выполнять доставку, когда код проталкивается в основную папку, а также ветки, начинающиеся с «RELEASE/v*». Я думаю, что, поскольку мы будем часто интегрироваться в main (теоретически), мы не обязательно хотим создавать полные «новые пакеты выпуска» для каждого коммита в main. Однако мы могли бы создавать «предварительные» пакеты NuGet для main для каждой фиксации, делая эти изменения доступными для веб-канала NuGet, но не помечая их как выпускную версию. В противном случае я настроил Nerdbank.GitVersioning для выпуска «выпускных» версий пакетов из веток «RELEASE/v*».

Сам файл действия CD будет выглядеть очень аналогично файлу CI, только с несколькими добавленными dotnet nuget... командами, показанными ниже:

.github/рабочие процессы/cicd.yml

name: Build, test, and deploy

on:
  push:
    branches:
      - 'main'
      - 'RELEASE/v**'


jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
      with:
        fetch-depth: 0

    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x
    
    - name: Restore dependencies
      run: dotnet restore
    
    - name: Build
      run: dotnet build -c Release --no-restore
    
    - name: Test
      run: dotnet test -c Release --no-restore --no-build --verbosity normal --filter "Category!=LongRunning"

    - name: Pack
      run: dotnet pack src/Orleans.SyncWork/Orleans.SyncWork.csproj -c Release --no-restore --no-build --include-symbols -p:SymbolPackageFormat=snupkg -o .

    - name: Push to NuGet
      run: dotnet nuget push *.nupkg --skip-duplicate -s https://api.nuget.org/v3/index.json -k ${{secrets.NUGET_API_KEY}}

В приведенном выше вы заметите, что это более 50% того же файла, что и CI. В какой-то момент я собирался потенциально рассмотреть составные действия, поэтому посмотрите, могу ли я вместо этого связать CI и CD, а не переопределять CI в файле CD; но у меня еще не было возможности исследовать это.

Помимо изменения события on (pull_request -> push), внизу есть две новые команды dotnet pack и dotnet nuget push. Команда dotnet pack используется для упаковки указанного проекта в файл .nupkg (и в данном случае snupkg для символов). Наконец, команда dotnet nuget push используется для отправки этих только что упакованных пакетов NuGet в фид, указанный на nuget.org. В этой команде вы также увидите часть команды {{secrets.NUGET_API_KEY}}, которая определяется как секрет репозитория и может использоваться для передачи секретной информации таким вещам, как рабочие процессы, в данном случае это мой ключ API NuGet. Эти секреты можно установить из репозитория Настройки -> Секреты:

еще сделать

«Выпуск ветки релиза» для меня по-прежнему является чем-то вроде ручного шага. Мне нужно запустить nbgv prepare-release из моей локальной среды, затем запустить созданную впоследствии ветку RELEASE/v* и обновить новую предварительную версию, созданную в main.

Возможно, это не имело смысла.

Если я работаю в main с предварительной версией «1.0-prerelease», когда я nbgv prepare-release, main будет (в качестве примера) обновлен до «1.1-prerelease» с веткой под названием «RELEASE/v1.0», созданной с релизная версия «1.0». Внедрение этих двух изменений в настоящее время создаст новый предварительный пакет «1.1-prerelease» и выпускной пакет «1.0», оба из которых будут содержать «один и тот же контент» на момент отправки. .

Я не уверен, как я отношусь к вышесказанному. Мне нравится автоматическая сборка и развертывание пакетов, но мне не нравится создавать релиз локально. Я мог бы, вероятно, создать управляемый вручную рабочий процесс, который выполнял бы эту подготовку к выпуску за меня, но тогда все еще была бы небольшая странность, связанная с немедленным выпуском предварительного пакета без каких-либо изменений по сравнению с «предыдущим» созданным предварительным пакетом, и создается новый пакет выпуска. Я еще не уверен, что такое «правильный поток», то, что у меня сейчас есть, действительно работает, просто кажется немного беспорядочным.

Возможно, со временем я рассмотрю рабочие процессы примерно так:

  • CI — продолжает выполняться на PR в основной
  • CICD — может выполняться для основной ветки и веток «RELEASE/v*», но не выполняется автоматически, как в настоящее время.
  • Рабочий процесс подготовки релиза — с этим рабочим процессом я хотел бы выполнить «локальные» шаги, которые я сейчас предпринимаю для подготовки релиза, но сделать это через действие github.

Рекомендации

Первоначально опубликовано на https://blog.kritner.com 29 ноября 2021 г.