Пост представляет собой контрольный список (checklist) и его реализацию при публикации Go приложения на Github'е. TLDR: Makefile как входная точка для выполнения основных действий Линтеры и тесты как инструменты повышающие качество кода Dockerfile как способ распространения приложения Github Actions как возможность автоматической сборки и выкладки приложения при новых изменениях Goreleaser как инструмент для публикации релизов и пакетов Результатом должно быть опубликованное приложение с настроенными инструментами которые позволяют легко сопровождать приложение в процессе его существования. В качестве реального примера я буду рассматривать утилиту pgcenter. Disclaimer: Приведенный список не являются абсолютной истинной и является лишь субъективным списком вещей к которым я пришел в процессе публикации приложении. Список может дополняться и изменяться. Все это одно большое ИМХО, если у вас есть альтернативный взгляд или вы уверены/знаете что какие-то вещи можно сделать еще лучше, обязательно дайте знать в комментах. Будем двигаться от простого к сложному. В случае встречи имен файлов, все они взяты относительно корня проекта где находится код приложения. Makefile хранит в себе служебные сценарии которые приходится часто выполнять при разработке приложения: выполнение линтеров и тестов сборка приложения запуск приложения сборка артефактов типа пакетов, docker образов и т.п. публикация артефактов в сторонние репозитории выполнение операций относительно внешних систем, например SQL миграции если речь идет о корпоративных приложениях В Makefile складываются рутинные операции выполнять которые приходится регулярно. Основная цель Makefile это помочь вам  не держать в голове все нужные команды, параметры и аргументы, а собрать и их в одном месте и при необходимости выполнить  их и получить результат. Позже Makefile также будет основным сценарием для запуска этих же рутинных процедур в CI/CD. Минимальная версия Makefile может выглядеть так: Давайте разберем важные моменты в приведенном Makefile. В начале файла указываем служебные переменные которые потребуются дальше. Переменных немного, называние программы и информация из Git которая используется при сборке. В частности коммит, тег и ветка которые через сборщика пробрасываются в код приложения  и используются для формирования версии программы. При таком подходе версия всегда берется из Git и не нужно хранить строку с версией в коде приложения, помнить о ней, своевременно обновлять. Отмечу что проброс переменных не автоматическая магия,  предполагается что в Git используются теги и также есть соответствующие правки в коде для приема Git переменных. Например 1 и 2. Следующий интересный момент это пункт help, он реализует справку для нашего Makefile - обратите внимание на комментарии  начинающиеся с двойной решетки после названий пунктов. Эти комментарии как раз и используются в качестве справки если вызвать make без аргументов или явно make help. Следующие пункты являются базовыми для жизненного цикла Go приложения: загрузка зависимостей, сборка и очистка каталога где хранится собранная программа. При желании список пунктов можно дополнить, что я и буду делать дальше по ходу текста. Следующий шаг добавление линтеров. Основная задача линтеров проверять код на предмет "странных" конструкций которые не соответствуют принятым стилям программирования или даже хуже, могут быть неоптимальными и приводить к каким-либо ошибкам. Наличие линтеров позволяют поддерживать хорошее качество кода, ориентироваться на правильный стиль написания кода принятый в языке. Использование линтеров необязательно, но крайне желательно. В go есть масса разных линтеров, я пришел пока к использованию golangci-lint и gosec. Первый включает в себя большой набор разных линтеров (включены при этом не все), второй является линтером с уклоном в соблюдение правил информационной безопасности. Выполнение линтеров также регулярная задача, поэтому также добавляем их в Makefile В приведенном случае для выполнения golangci-lint выставлен таймаут, через флаг -E включаются дополнительные линтеры. Выполнение gosec особо ничем не примечательно, просто рекурсивный обход каталогов. Очевидно что golangci-lint и gosec должны быть установлены в системе. Их установка проста, описывать тут не буду. Также код сопровождается тестами, добавим и их выполнение тоже.  Команда запускает тесты, формирует файл с покрытием кода этими тестами и печатает результат покрытия. Следующим этапом является Dockerfile, который позволит собирать Docker образы нашего приложения. Сборка образа состоит из двух этапов, сборка приложения и его копирование в пустой образ. Таким двухэтапным образом можно упаковывать приложения которые не требуют внешних зависимостей типа библиотек, сертификатов, других программ или чего-то аналогичного.  Помимо сборки Docker образа потребуется место откуда другие пользователи смогут его забрать, например этим местом может быть Docker Hub. Для размещения там образа потребуется аккаунт и реквизиты (логин/пароль). Сборка и публикация образа также регулярная операция, поэтому добавляем команды в Makefile. Обратите внимание, что используется переменная TAG которая определяется в начале Makefile. Теперь когда у нас есть способ для сборки приложения (Makefile) и публикации (Dockerfile), нам нужен механизм который поможет автоматически выполнять сборку и публикацию обновлений приложения. Здесь нам поможет Github Actions. Однако в этом месте мы начинаем соприкасаться с организацией процесса, как новые изменения становятся частью существующего кода. Тема довольно обширная, об этом написано много постов, выработаны целые подходы со своими правилами. Поэтому те кто в теме уже  и так все знают, а кто не в теме пусть отправляется на поиски чтива Git Flow, Github Flow, Gitlab Flow и их сравнение друг с другом. В нашем случае все просто. Все изменения будут вливаться в master ветку. От ветки master мы создадим release ветку которая и будет источником релизов. Когда мы захотим сделать релиз, просто создадим новый тег и подтянем все изменения из master в release. Теперь когда у нас есть понимание того как вносить изменения и делать релизы можно перейти к настройке workflow в Github Actions. Кто знает нормальный однословный русский перевод слову wokflow дайте знать в комментариях. Будет два workflow: Default (.github/workflows/default.yml) - это flow по-умолчанию, выполняет тесты при поступлении новых изменений. Release (.github/workflows/release.yml) - это релизный flow делает тесты, сборку Docker образов и пакетов для пакетных менеджеров. Этот workflow запускается при наступлении push и pull request событий в ветке master. Здесь всего одна задача (job), запуск линтеров и тестов. Github Actions имеет хорошие возможности для кастомизации и позволяют описывать очень сложные сценарии. В нашем случае таким примером кастомизации является запуск и выполнение в специально подготовленном контейнере где есть все необходимое для осуществления тестов. Второй workflow. Релизный workflow чуть больше, и запускается он при push событиях в release ветке. Этот workflow также включает в себя выполнение линтеров и тестов. Но также тут есть еще две задачи - build и goreleaser.  В задаче build выполняется сборка и публикация Docker образа. Обратите внимание, что используются секреты, которые предварительно нужно указать в разделе Secrets, в настройках Github репозитория.  В задаче goreleaser выполняется публикация релиза в разделе Releases репозитория. Настройки goreleaser определим позже. Здесь также используется секрет GITHUB_TOKEN его указывать нигде не нужно, он создается автоматически для нужд workflow. Последний шаг это публикация релиза и дополнительная сборка пакетов. Кроме Docker образов существуют способы распространения с помощью пакетных менеджеров. Наиболее распространенные это deb и rpm пакеты. Есть и другие варианты, но для меня они экзотичны и их я рассматривать не буду. Для всего этого нам потребуется goreleaser который и сделает всю работу по сборке. Настройки определяются в .goreleaser.yml Важным моментом является то что goreleaser не имеет интеграции с Makefile и сборку нужно описывать отдельно в формате goreleaser правил. Поэтому важно описать сборку точно так же как это осуществляется в Makefile, т.е. указать все те же флаги, переменные окружения и т.п. В секции nfpms описываем метаданные пакета и указываем необходимые форматы пакетов.   Собственно на этом все. Можно фиксировать изменения, пушить в репозиторий, перейти в Actions и наблюдать за тем как выполняются workflow. При успешном выполнении, можем создать новый тег, и влить изменения в релизную ветвь и также понаблюдать за прогрессом в Actions. Спасибо за внимание, если у кого есть дополнения, замечания, вопросы, пожелания - Go в комменты. Список в gist.github.com golangci-lint gosec Docker multi-stage build GitHub Actions Quick Start Goreleaser: один и два PostgreSQL DBA