Русский
Русский
English
Статистика
Реклама

Публикация Go приложения в GitHub

Пост представляет собой контрольный список (checklist) и его реализацию при публикации Go приложения на Github'е.

TLDR:

  • Makefile как входная точка для выполнения основных действий

  • Линтеры и тесты как инструменты повышающие качество кода

  • Dockerfile как способ распространения приложения

  • Github Actions как возможность автоматической сборки и выкладки приложения при новых изменениях

  • Goreleaser как инструмент для публикации релизов и пакетов

Результатом должно быть опубликованное приложение с настроенными инструментами которые позволяют легко сопровождать приложение в процессе его существования. В качестве реального примера я буду рассматривать утилиту pgcenter.


Disclaimer: Приведенный список не являются абсолютной истинной и является лишь субъективным списком вещей к которым я пришел в процессе публикации приложении. Список может дополняться и изменяться. Все это одно большое ИМХО, если у вас есть альтернативный взгляд или вы уверены/знаете что какие-то вещи можно сделать еще лучше, обязательно дайте знать в комментах.

Будем двигаться от простого к сложному. В случае встречи имен файлов, все они взяты относительно корня проекта где находится код приложения.

Makefile

Makefile хранит в себе служебные сценарии которые приходится часто выполнять при разработке приложения:

  • выполнение линтеров и тестов

  • сборка приложения

  • запуск приложения

  • сборка артефактов типа пакетов, docker образов и т.п.

  • публикация артефактов в сторонние репозитории

  • выполнение операций относительно внешних систем, например SQL миграции если речь идет о корпоративных приложениях В Makefile складываются рутинные операции выполнять которые приходится регулярно. Основная цель Makefile это помочь вам не держать в голове все нужные команды, параметры и аргументы, а собрать и их в одном месте и при необходимости выполнить их и получить результат. Позже Makefile также будет основным сценарием для запуска этих же рутинных процедур в CI/CD. Минимальная версия Makefile может выглядеть так:

PROGRAM_NAME = pgcenterCOMMIT=$(shell git rev-parse --short HEAD)BRANCH=$(shell git rev-parse --abbrev-ref HEAD)TAG=$(shell git describe --tags |cut -d- -f1)LDFLAGS = -ldflags "-X main.gitTag=${TAG} -X main.gitCommit=${COMMIT} -X main.gitBranch=${BRANCH}".PHONY: help clean dep build install uninstall .DEFAULT_GOAL := helphelp: ## Display this help screen.@echo "Makefile available targets:"@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  * \033[36m%-15s\033[0m %s\n", $$1, $$2}'dep: ## Download the dependencies.go mod downloadbuild: dep ## Build pgcenter executable.mkdir -p ./binCGO_ENABLED=0 GOOS=linux GOARCH=${GOARCH} go build ${LDFLAGS} -o bin/${PROGRAM_NAME} ./cmdclean: ## Clean build directory.rm -f ./bin/${PROGRAM_NAME}rmdir ./bin

Давайте разберем важные моменты в приведенном Makefile.

  • В начале файла указываем служебные переменные которые потребуются дальше. Переменных немного, называние программы и информация из Git которая используется при сборке. В частности коммит, тег и ветка которые через сборщика пробрасываются в код приложения и используются для формирования версии программы. При таком подходе версия всегда берется из Git и не нужно хранить строку с версией в коде приложения, помнить о ней, своевременно обновлять. Отмечу что проброс переменных не автоматическая магия, предполагается что в Git используются теги и также есть соответствующие правки в коде для приема Git переменных. Например 1 и 2.

  • Следующий интересный момент это пункт help, он реализует справку для нашего Makefile - обратите внимание на комментарии начинающиеся с двойной решетки после названий пунктов. Эти комментарии как раз и используются в качестве справки если вызвать make без аргументов или явно make help.

  • Следующие пункты являются базовыми для жизненного цикла Go приложения: загрузка зависимостей, сборка и очистка каталога где хранится собранная программа.

При желании список пунктов можно дополнить, что я и буду делать дальше по ходу текста.

Линтеры и тесты

Следующий шаг добавление линтеров. Основная задача линтеров проверять код на предмет "странных" конструкций которые не соответствуют принятым стилям программирования или даже хуже, могут быть неоптимальными и приводить к каким-либо ошибкам. Наличие линтеров позволяют поддерживать хорошее качество кода, ориентироваться на правильный стиль написания кода принятый в языке. Использование линтеров необязательно, но крайне желательно. В go есть масса разных линтеров, я пришел пока к использованию golangci-lint и gosec. Первый включает в себя большой набор разных линтеров (включены при этом не все), второй является линтером с уклоном в соблюдение правил информационной безопасности.

Выполнение линтеров также регулярная задача, поэтому также добавляем их в Makefile

lint: dep ## Lint the source filesgolangci-lint run --timeout 5m -E golintgosec -quiet ./...

В приведенном случае для выполнения golangci-lint выставлен таймаут, через флаг -E включаются дополнительные линтеры. Выполнение gosec особо ничем не примечательно, просто рекурсивный обход каталогов.

Очевидно что golangci-lint и gosec должны быть установлены в системе. Их установка проста, описывать тут не буду.

Также код сопровождается тестами, добавим и их выполнение тоже.

test: dep ## Run testsgo test -race -p 1 -timeout 300s -coverprofile=.test_coverage.txt ./... && \    go tool cover -func=.test_coverage.txt | tail -n1 | awk '{print "Total test coverage: " $$3}'@rm .test_coverage.txt

Команда запускает тесты, формирует файл с покрытием кода этими тестами и печатает результат покрытия.

Dockerfile

Следующим этапом является Dockerfile, который позволит собирать Docker образы нашего приложения.

# stage 1: buildFROM golang:1.15 as buildLABEL stage=intermediateWORKDIR /appCOPY . .RUN make build# stage 2: scratchFROM scratch as scratchCOPY --from=build /app/bin/pgcenter /bin/pgcenterCMD ["pgcenter"]

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

Помимо сборки Docker образа потребуется место откуда другие пользователи смогут его забрать, например этим местом может быть Docker Hub. Для размещения там образа потребуется аккаунт и реквизиты (логин/пароль).

Сборка и публикация образа также регулярная операция, поэтому добавляем команды в Makefile.

docker-build: ## Build docker imagedocker build -t lesovsky/pgcenter:${TAG} .docker image prune --force --filter label=stage=intermediatedocker-push: ## Push docker image to registrydocker push lesovsky/pgcenter:${TAG}

Обратите внимание, что используется переменная TAG которая определяется в начале Makefile.

Github Actions

Теперь когда у нас есть способ для сборки приложения (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.

---name: Releaseon:  push:    branches: [ release ]jobs:  test:    runs-on: ubuntu-latest    container: lesovsky/pgcenter-testing:v0.0.1    steps:      - name: Checkout code        uses: actions/checkout@v2      - name: Prepare test environment        run: prepare-test-environment.sh      - name: Run lint        run: make lint      - name: Run test        run: make test  build:    runs-on: ubuntu-latest    needs: test    steps:      - name: Checkout code        uses: actions/checkout@v2        with:          fetch-depth: 0      - name: Build image        run: make docker-build      - name: Log in to Docker Hub        run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}      - name: Push image to Docker Hub        run: make docker-push  goreleaser:    runs-on: ubuntu-latest    needs: [ test, build ]    steps:      - uses: actions/checkout@v2        with:          fetch-depth: 0      - uses: actions/setup-go@v2        with:          go-version: 1.15      - uses: goreleaser/goreleaser-action@v2        with:          version: latest          args: release --rm-dist        env:          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Релизный workflow чуть больше, и запускается он при push событиях в release ветке. Этот workflow также включает в себя выполнение линтеров и тестов. Но также тут есть еще две задачи - build и goreleaser.

В задаче build выполняется сборка и публикация Docker образа. Обратите внимание, что используются секреты, которые предварительно нужно указать в разделе Secrets, в настройках Github репозитория.

В задаче goreleaser выполняется публикация релиза в разделе Releases репозитория. Настройки goreleaser определим позже. Здесь также используется секрет GITHUB_TOKEN его указывать нигде не нужно, он создается автоматически для нужд workflow.

Goreleaser

Последний шаг это публикация релиза и дополнительная сборка пакетов. Кроме Docker образов существуют способы распространения с помощью пакетных менеджеров. Наиболее распространенные это deb и rpm пакеты. Есть и другие варианты, но для меня они экзотичны
и их я рассматривать не буду. Для всего этого нам потребуется goreleaser который и сделает всю работу по сборке. Настройки определяются в .goreleaser.yml

before:  hooks:  - make depbuilds:  - binary: pgcenter    main: ./cmd    goarch:      - amd64    goos:      - linux    env:      - CGO_ENABLED=0    ldflags:      - -a -installsuffix cgo      - -X main.gitTag={{.Tag}} -X main.gitCommit={{.Commit}} -X main.gitBranch={{.Branch}}archives:  - builds: [pgcenter]changelog:  sort: ascnfpms:  - vendor: pgcenter    homepage: https://github.com/lesovsky/pgcenter    maintainer: Alexey Lesovsky    description: Command-line admin tool for observing and troubleshooting Postgres.    license: BSD-3    formats: [ deb, rpm ]

Важным моментом является то что goreleaser не имеет интеграции с Makefile и сборку нужно описывать отдельно в формате goreleaser правил. Поэтому важно описать сборку точно так же как это осуществляется в Makefile, т.е. указать все те же флаги, переменные окружения и т.п. В секции nfpms описываем метаданные пакета и указываем необходимые форматы пакетов.

Собственно на этом все. Можно фиксировать изменения, пушить в репозиторий, перейти в Actions и наблюдать за тем как выполняются workflow. При успешном выполнении, можем создать новый тег, и влить изменения в релизную ветвь и также понаблюдать за прогрессом в Actions.

Спасибо за внимание, если у кого есть дополнения, замечания, вопросы, пожелания - Go в комменты.

Ссылки

Источник: habr.com
К списку статей
Опубликовано: 15.02.2021 10:17:19
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Go

Github

Github actions

Makefile

Goreleaser

Golangci-lint

Gosec

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru