Сегодня я расскажу вам как можно опубликовать своё Spring Boot приложение в GitHub Packages с помощью GitHub Actions. Вот так. В общем-то всё. Вот. Спасибо за внимание. Ну, а здесь, для самых любознательных, приведу немного информации о том, что делает этот GitHub Action. Прежде чем перейти к самому GitHub Action приведу сначала то, что он должен уметь делать. Он должен собрать докер образ. Выложить его в GitHub Packages. В качестве примера собирать буду вот этот проект. Подробнее о нём я писал в этой статье. Здесь лишь отмечу, что в качестве системы сборки в нём используется gradle. Как же можно собрать docker-образ своего приложения? Ну конечно же, написать Dockerfile! Делать я этого, конечно же, не буду, а воспользуюсь замечательным проектом Cloud Native Buildpacks. Что же в этом проекте такого замечательного?!. В Spring Boot имеется поддержка этого проекта, что позволяет собрать docker-образ без написания Dockerfile всего одной командой. Например, при использовании в качестве сборщика gradle это команда будет выглядеть так: А при использовании maven - так: В результате выполнения команды будет создан и локально опубликован docker-образ. Версия java определиться автоматически из настроек сборщика, а в качестве JDK по умолчанию будет использоваться сборка BellSoft Liberica. Параметры по умолчанию можно изменить. Как это сделать можно посмотреть в настройках плагина для соответствующей системы сборки (maven, gradle). Возможно, через конфигурацию плагинов получится изменить не все параметры, которые есть в buildpack. В этом случае собирать образ можно будет с помощью утилиты pack. Подробнее о сборке java приложений с помощью этой утилиты можно почитать, например, тут. Правда, на мой взгляд, это будет уже не так лаконично, как было с помощью соответствующих плагинов. Изначально в проекте у меня была java 15. Но при вызове таскиbootBuildImage я получал ошибку вида: Как оказалось, ошибка была связана с тем, что у BellSoft нет сборки java 15. Но когда-то она была, но после того как они собрали 16-ю версию, 15-ю решили удалить. И это выглядит, мягко говоря, не очень надёжным. Ну и с выходом 17-й так же удалили и 16-ю. Немного о GitHub Actions. Это штука, которая позволяет автоматизировать рабочий процесс прямо в GitHub (настроить CI/CD). Например, с помощью неё можно собирать приложение по коммиту, где-то публиковать, прогонять тесты и делать многое-многое другое. Переходим к сборке. Для этого нужно создать свой action, который будет этим заниматься. Либо можно добавить существующий из marketplace. Мне, правда, не удалось найти нужный, который бы использовал для сборки docker-образа Cloud Native Buildpacks через gradle-плагин, но, возможно, на момент, когда вы читаете эту статью, этот плагин там есть, либо он и был, а я просто плохо искал. Для создания своего action в GitHub-репозитории нужно перейти на вкладку Actions. Здесь можно либо самостоятельно настроить workflow нажав на set up a workflow yourself. Либо можно выбрать уже готовое workflow из предложенного списка. Проще всего, конечно же, выбрать уже готовое workflow и видоизменить его требуемым образом. Так как для сборки проекта используется gradle, то и взять лучше всего workflow для gradle-проекта. На текущих момент чтобы найти это workflow, нужно пролистать страницу вниз до кнопки More continuous integration workflows... и нажать на неё. Среди предложенных workflow нужно найти Java with Gradle и нажать на кнопку Set up this workflow. После этого мы перейдём на страницу создания нового action для проекта, на которой уже будет доступен код, который можно редактировать. В верху страницы для редактирования доступно имя файла, в котором будет находится код этого action. По умолчанию файл будет называться gradle.yml. Так как данный action будет заниматься сборкой проекта, предлагаю переименовать его в build.yml. Ниже представлен код данного action, который был сгенерирован автоматически. Немного разберём что здесь происходит. name: Java CI with Gradle - имя workflow, будет отображаться на страничке Actions в GitHub. Блок on описывает, какие события должны запускать данный workflow. В данном случае это push и pull_request, кроме того можно указать, в каких ветках должны происходить данные события с помощью branches. Более подробно о синтаксисе можно почитать в документации. jobs. В этом блоке описываются job'ы (например, сборка, тестирование и т.п.). Их может быть несколько. По умолчанию они будут запускаться параллельно, но документация говорит, что можно запустить их последовательно. В данном случае есть лишь одна job, которая называется build. runs-on. В этом блоке указывается runner, т.е., по сути, виртуальная машина,  на которой будет выполняться job. В данном случае это ubuntu-latest. steps. Блок, в котором описаны шаги, из которых состоит job. Каждый шаг является отдельным GitHub Action или shell-командой. uses: actions/checkout@v2. uses означает, что требуется взять GitHub Action, далее указывается имя GitHub Action, в данном случае actions/checkout@v2. Данный GitHub Action вытягивает код из репозитория в runner. uses: actions/setup-java@v2. GitHub Action actions/setup-java@v2 устанавливает java в runner. С помощью with можно передавать параметры для данного GitHub Action. java-version задаёт версию java. distribution задаёт дистрибутив. cache: gradle указывает, что необходимо сохранять локальный кэш Gradle в инфраструктуре GitHub Actions таким образом, чтобы он использовался в будущих запусках workflow. Подробнее о допустимых значениях параметров можно почитать на страничке данного GitHub Action. Так же для данного шага указано name. Будет отображаться в логах выполнения job на GitHub. run: chmod +x gradlew. run позволяет выполнять shell-команды. Команда chmod +x gradlew даёт права на выполнение файла gradlew. run: ./gradlew build - cобирает проект. Изменим скрипт таким образом чтобы он делал требуемые нам действия. В результате получится так. Разберём что изменилось. on: [push]. Данный workflow теперь запускает любое событие push. В блоке env объявлена переменна окружения IMAGE_NAME, которая используется далее в скрипте. java-version: '17'. Используется 17-я версия java. Так же, как можно заметить, в данной конфигурации GitHub Action actions/setup-java@v2 не используется cache для gradle. run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin. Команда помогает залогиниться в GitHub Packages. С помощью docker login логинимся на ghcr.io, с помощью параметра -u передаём логин пользователя GitHub. Сам логин берём из github-контекста, в котором хранится различная информация, как о workflow, так и о событии которое его запустило. Параметр --password-stdin говорит о том, что пароль, а точнее токен, будет получен из stdin. С помощью echo "${{ secrets.GITHUB_TOKEN }}" передаём токен в stdin. В качестве токена здесь используется GITHUB_TOKEN. Этот токен создаётся в автоматическом режиме в начале каждого запуска workflow, и он доступен как секрет secrets.GITHUB_TOKEN либо его можно получить из github-контекста как github.token. GITHUB_TOKEN не всесилен. Его набор прав ограничен из коробки. Но его можно расширить с помощью блока permissions. Так же, например, правами на packages можно управлять на странице настроек пакета (думаю, другими правами так же можно управлять на соответствующих страницах). На этой странице в блоке Manage Actions access можно добавлять репозитории и давать им права на этот пакет. И если, например, дать права только на чтение репозиторию, то он не сможет с GITHUB_TOKEN сюда публиковать, не смотря на то что у GITHUB_TOKEN по умолчанию есть доступ на запись. ./gradlew bootBuildImage --imageName=$IMAGE_NAME - собирает docker-образ с помощью Cloud Native Buildpacks. По умолчанию имя docker-образа будет docker.io/library/${project.name}:${project.version}, а точнее, в данном конкретном примере, docker.io/library/list-keep:0.0.1-SNAPSHOT. Гораздо более удобно было бы, на мой взгляд, если бы версия была latest. Сделать это можно, переопределив имя docker-образ с помощью параметра --imageName. В этот параметр можно передать как полное название docker-образа - docker.io/library/list-keep:latest, так и часть его, например, list-keep. В этом случае docker.io/library и latest (если версия не указана, то по умолчанию выставляется latest) допишутся автоматически. Далее идёт многострочный скрипт публикации docker-образа в GitHub Packages. Чтобы скрипт был многострочным, необходимо в самом начале скрипта указать символ |. IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME превратится в ghcr.io/vanbv/list-keep. Это адрес пакета в GitHub Packages, куда будет опубликован собранный docker-образ. VERSION=latest. В качестве версии будем использовать latest. Если требуется делать что-то более сложное, например, проставлять версию в соответствии с версией приложения, то в документации GitHub Actions есть неплохой пример того, как это можно сделать. docker tag $IMAGE_NAME $IMAGE_ID:$VERSION превратится в docker tag list-keep ghcr.io/vanbv/list-keep:latest. Команда docker tag создаст тег ghcr.io/vanbv/list-keep:latest, который будет ссылаться на list-keep:latest (т.к. версия не указана, то по умолчанию возьмётся latest). docker push $IMAGE_ID:$VERSION превратится в docker push ghcr.io/vanbv/list-keep:latest. docker push опубликует docker-образ в GitHub Packages. Вот, в общем-то, и всё. Надеюсь, было интересно и полезно. Берегите там себя. Java Web Developer