GitHub API в Java
Я знаю, что GitHub используется для отслеживания версий проекта. Как им пользоваться? И можно ли сделать так чтобы, к примеру, я написал код на java, загрузил его в свой репозиторий, и у клиента выполняется этот код выполняется? Если можно, то как?
Отслеживать
user236980
задан 13 сен 2013 в 23:19
delphikettle delphikettle
1,330 4 4 золотых знака 24 24 серебряных знака 48 48 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
GitHub — это хостинг Git — это система управления версиями Jenkins — это инструмент, для непрерывной интеграции
- установить git и приблизительно научится им пользоваться
- выбрать сервер для хранения кода, это может быть github, но он платный для приватных проектов (есть много хороших бесплатных аналогов)
- попробовать разобраться с jenkins — с его помощью можно «выполнять код у клиента», делать билды по разписанию, гонять тесты
Отслеживать
user181100
ответ дан 14 сен 2013 в 7:05
12.4k 1 1 золотой знак 20 20 серебряных знаков 43 43 бронзовых знака
Можно поподробнее о бесплатных аналогах?
14 сен 2013 в 9:42
xp-dev.com, assembla.com, bitbucket.org
14 сен 2013 в 9:57
- github
- java
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.21.1314
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
6.5 GitHub — Создание сценариев GitHub
Итак, мы рассмотрели все основные функции и рабочие процессы GitHub, но у любой большой группы или проекта могут быть настройки, которые они могут захотеть сделать, или внешние сервисы, которые они могут захотеть интегрировать.
К счастью для нас, GitHub действительно можно взломать во многих отношениях. В этом разделе мы расскажем, как использовать систему хуков GitHub и его API, чтобы заставить GitHub работать так, как мы хотим.
Сервисы и хуки
Раздел Hooks and Services администрирования репозитория GitHub — это самый простой способ взаимодействия GitHub с внешними системами.
Сервисы
Сначала мы рассмотрим сервисы. Интеграцию хуков и сервисов можно найти в разделе «Настройки» («Settings») вашего репозитория, где ранее мы рассматривали возможность добавления соавторов и изменения ветки вашего проекта по умолчанию. На вкладке «Вебхуки и сервисы» («Webhooks and Services») вы увидите что-то вроде Раздел настройки сервисов и хуков.
Рисунок 129. Раздел настройки сервисов и хуков
Вы можете выбирать из десятков сервисов, большинство из которых интегрируются в другие коммерческие системы и системы с открытым исходным кодом. Большинство из них предназначены для сервисов непрерывной интеграции, систем отслеживания ошибок и проблем, систем чатов и систем документации. Мы рассмотрим настройку очень простого хука электронной почты. Если вы выберете «email» в раскрывающемся списке «Добавить сервис» («Add Service»), вы получите экран конфигурации, например Конфигурация службы электронной почты.

Рисунок 130. Конфигурация службы электронной почты
В этом случае, если мы нажмем кнопку «Добавить сервис» («Add Service»), указанный нами адрес электронной почты будет получать электронное письмо каждый раз, когда кто-то отправляет в репозиторий. Сервисы могут прослушивать множество различных типов событий, но большинство из них прослушивают только push-события, а затем что-то делают с этими данными.
Если вы используете систему, которую хотите интегрировать с GitHub, вам следует проверить здесь, чтобы узнать, доступна ли существующая интеграция сервиса. Например, если вы используете Jenkins для запуска тестов своей кодовой базы, вы можете включить интеграцию встроенного сервиса Jenkins, чтобы запускать тестовый прогон каждый раз, когда кто-то отправляет данные в ваш репозиторий.
Хуки
Если вам нужно что-то более конкретное или вы хотите интегрироваться с сервисом или сайтом, не включённым в этот список, вы можете вместо этого использовать более общую систему хуков. Хуки репозитория GitHub довольно просты. Вы указываете URL-адрес, и GitHub отправит полезные данные HTTP на этот URL-адрес для любого события, которое вы хотите.
Как правило, это работает так: вы можете настроить небольшой веб-сервис для прослушивания полезных данных хука GitHub, а затем что-то делать с данными, когда они будут получены.
Чтобы включить хук, вы нажимаете кнопку «Добавить вебхук» («Add webhook») в Раздел настройки сервисов и хуков. Это приведет вас на страницу, которая выглядит как Конфигурация вебхука.

Рисунок 131. Конфигурация вебхука
Конфигурация вебхука довольно проста. В большинстве случаев вы просто вводите URL и секретный ключ и нажимаете «Добавить вебхук» («Add webhook»). Есть несколько вариантов, для каких событий вы хотите, чтобы GitHub отправлял вам полезные данные — по умолчанию вы получаете полезные данные только для события push , когда кто-то отправляет новый код в любую ветку вашего репозитория.
Давайте рассмотрим небольшой пример веб-сервиса, который вы можете настроить для обработки вебхука. Мы будем использовать веб-фреймворк Ruby Sinatra, так как он довольно лаконичен, и вы сможете легко увидеть, что мы делаем.
Допустим, мы хотим получать электронное письмо, если конкретный человек отправляет на определённую ветку нашего проекта, изменённый определённый файл. Мы могли бы довольно легко сделать это с помощью такого кода:
require 'sinatra' require 'json' require 'mail' post '/payload' do push = JSON.parse(request.body.read) # parse the JSON # gather the data we're looking for pusher = push["pusher"]["name"] branch = push["ref"] # get a list of all the files touched files = push["commits"].map do |commit| commit['added'] + commit['modified'] + commit['removed'] end files = files.flatten.uniq # check for our criteria if pusher == 'schacon' && branch == 'ref/heads/special-branch' && files.include?('special-file.txt') Mail.deliver do from 'tchacon@example.com' to 'tchacon@example.com' subject 'Scott Changed the File' body "ALARM" end end end
Здесь мы берем полезные данные JSON, которые доставляет нам GitHub, и ищем, кто их отправил, в какую ветку они отправили и какие файлы были затронуты во всех отправленных коммитах. Затем мы проверяем это на соответствие нашим критериям и отправляем электронное письмо, если оно соответствует.
Чтобы разработать и протестировать что-то подобное, у вас есть хорошая консоль разработчика на том же экране, где вы устанавливаете связь. Вы можете увидеть последние несколько доставок, которые GitHub пытался сделать для этого вебхука. Для каждого хука вы можете узнать, когда он был доставлен, если он был успешным, а также тело и заголовки как для запроса, так и для ответа. Это делает невероятно простым тестирование и отладку ваших хуков.

Рисунок 132. Информация об отладке вебхуков
Другая замечательная особенность этого заключается в том, что вы можете повторно доставить любые полезные данные, чтобы легко протестировать свой сервис.
Для получения дополнительной информации о том, как писать вебхуки и обо всех различных типах событий, которые вы можете прослушивать, перейдите к документации GitHub Developer по адресу https://developer.github.com/webhooks/.
GitHub API
Сервисы и хуки дают вам возможность получать push-уведомления о событиях, происходящих в ваших репозиториях, но что, если вам нужна дополнительная информация об этих событиях? Что, если вам нужно автоматизировать что-то вроде добавления соавторов или маркировки проблем?
Вот где API GitHub пригодится. GitHub имеет множество конечных точек API для автоматического выполнения почти всего, что вы можете делать на веб-сайте. В этом разделе мы узнаем, как пройти аутентификацию и подключиться к API, как прокомментировать проблему и как изменить статус запроса на слияние через API.
Основное использование
Самое простое, что вы можете сделать, — это отправить простой запрос GET на конечную точку, не требующую аутентификации. Это может быть пользовательская или доступная только для чтения информация о проекте с открытым исходным кодом. Например, если мы хотим узнать больше о пользователе с именем «schacon», мы можем запустить что-то вроде этого:
$ curl https://api.github.com/users/schacon < "login": "schacon", "id": 70, "avatar_url": "https://avatars.githubusercontent.com/u/70", # … "name": "Scott Chacon", "company": "GitHub", "following": 19, "created_at": "2008-01-27T17:19:28Z", "updated_at": "2014-06-10T02:37:23Z" >
Существует множество подобных конечных точек для получения информации об организациях, проектах, проблемах, коммитах — почти обо всём, что вы можете публично увидеть на GitHub. Вы даже можете использовать API для визуализации произвольного Markdown или найти шаблон .gitignore .
$ curl https://api.github.com/gitignore/templates/Java < "name": "Java", "source": "*.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* " >
Комментирование проблемы
Однако, если вы хотите выполнить какое-либо действие на веб-сайте, например прокомментировать проблему или запрос на слияние, или если вы хотите просмотреть или взаимодействовать с частным контентом, вам необходимо пройти аутентификацию.
Существует несколько способов аутентификации. Вы можете использовать обычную аутентификацию только с вашим именем пользователя и паролем, но, как правило, лучше использовать токен личного доступа. Вы можете сгенерировать его на вкладке «Приложения» («Applications») на странице настроек.

Рисунок 133. Сгенерируйте токен доступа на вкладке «Приложения» («Applications») на странице настроек
Он спросит вас, какие области вы хотите для этого токена и описание. Обязательно используйте хорошее описание, чтобы вам было удобно удалять токен, когда ваш скрипт или приложение больше не используются.
GitHub покажет вам токен только один раз, поэтому обязательно скопируйте его. Теперь вы можете использовать это для аутентификации в своем скрипте вместо использования имени пользователя и пароля. Это хорошо, потому что вы можете ограничить объём того, что вы хотите сделать, и токен может быть отозван.
Это также имеет дополнительное преимущество в виде увеличения лимита скорости. Без аутентификации вы будете ограничены 60 запросами в час. Если вы аутентифицируетесь, вы можете делать до 5000 запросов в час.
Итак, давайте воспользуемся им, чтобы прокомментировать одну из наших проблем. Допустим, мы хотим оставить комментарий к конкретной проблеме № 6 (Issue #6). Для этого нам нужно выполнить HTTP-запрос POST к repos///issues//comments с токеном, который мы только что сгенерировали в качестве заголовка авторизации.
$ curl -H "Content-Type: application/json" \ -H "Authorization: token TOKEN" \ --data '' \ https://api.github.com/repos/schacon/blink/issues/6/comments < "id": 58322100, "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100", . "user": < "login": "tonychacon", "id": 7874698, "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2", "type": "User", >, "created_at": "2014-10-08T07:48:19Z", "updated_at": "2014-10-08T07:48:19Z", "body": "A new comment, :+1:" >
Теперь, если вы перейдёте к этой проблеме, вы увидите комментарий, который мы только что успешно разместили, например Комментарий, отправленный из GitHub API.

Рисунок 134. Комментарий, отправленный из GitHub API
Вы можете использовать API, чтобы делать практически всё, что вы можете делать на веб-сайте: создавать и устанавливать вехи (milestones), назначать людей для проблем и запросов на слияние, создавать и изменять метки, получать доступ к данным коммитов, создавать новые коммиты и ветки, открывать, закрывать или объединение запросов на слияние, создание и редактирование команд, комментирование строк кода в запросе на слияние, поиск по сайту и так далее.
Изменение статуса запроса на слияние
Есть ещё один последний пример, который мы рассмотрим, так как он действительно полезен, если вы работаете с запросами на слияние. С каждым коммитом может быть связан один или несколько статусов, и существует API для добавления и запроса этого статуса.
Большинство сервисов непрерывной интеграции и тестирования используют этот API для реагирования на отправку путём тестирования кода, который был отправлен, а затем сообщают, прошел ли этот коммит все тесты. Вы также можете использовать это, чтобы проверить, правильно ли отформатировано сообщение о коммите, следовал ли отправитель всем вашим рекомендациям по вкладу, был ли коммит действительно подписан — что угодно.
Допустим, вы настроили вебхук в своём репозитории, который обращается к небольшому веб-сервису, который проверяет наличие строки «Signed-off-by» в сообщении коммита.
require 'httparty' require 'sinatra' require 'json' post '/payload' do push = JSON.parse(request.body.read) # parse the JSON repo_name = push['repository']['full_name'] # look through each commit message push["commits"].each do |commit| # look for a Signed-off-by string if /Signed-off-by/.match commit['message'] state = 'success' description = 'Successfully signed off!' else state = 'failure' description = 'No signoff found.' end # post status to GitHub sha = commit["id"] status_url = "https://api.github.com/repos/#/statuses/#" status = < "state" =>state, "description" => description, "target_url" => "http://example.com/how-to-signoff", "context" => "validate/signoff" > HTTParty.post(status_url, :body => status.to_json, :headers => < 'Content-Type' =>'application/json', 'User-Agent' => 'tonychacon/signoff', 'Authorization' => "token #" > ) end end
Надеюсь, следовать этому довольно просто. В этом обработчике вебхука мы просматриваем каждый только что отправленный коммит, ищем строку «Signed-off-by» в сообщении коммита и, наконец, отправляем POST через HTTP в /repos// /statuses/ Конечная точка API со статусом.
В этом случае вы можете отправить состояние («успех» (‘success’), «сбой» (‘failure’), «ошибка» (‘error’)), описание того, что произошло, целевой URL-адрес, по которому пользователь может перейти для получения дополнительной информации, и «контекст» в случае наличия нескольких статусов за один коммит. Например, сервис тестирования может предоставить статус, а сервис проверки, подобная этой, также может предоставить статус — поле «контекст» — это то, как они различаются.
Если кто-то откроет новый запрос на слияние на GitHub и этот хук настроен, вы можете увидеть что-то вроде Статус коммита через API.

Рисунок 135. Статус коммита через API
Теперь вы можете увидеть маленькую зеленую галочку рядом с коммитом, в сообщении которой есть строка «Signed-off-by», и красным крестиком тот коммит, который автор забыл подписать. Вы также можете видеть, что запрос на слияние принимает статус последнего коммита в ветке и предупреждает вас, если это сбой. Это действительно полезно, если вы используете этот API для результатов тестирования, чтобы случайно не объединить что-то, где последний коммит не прошел тесты.
A2.3 Приложение B: Встраивание Git в ваши приложения — JGit
Если вы хотите использовать Git из Java-программ, существует библиотека для работы с Git, называемая JGit. Она достаточно полно реализует функциональность Git, написана на чистом Java и широко используется Java сообществом. Проект JGit находится под опекой Eclipse и расположен по адресу https://www.eclipse.org/jgit.
Приступая к работе
Существует несколько способов добавить JGit в проект и начать писать код с использованием предоставляемого API. Возможно, самый простой путь — использование Maven: подключение библиотеки происходит путём добавления следующих строк в секцию в вашем pom.xml:
org.eclipse.jgit org.eclipse.jgit 3.5.0.201409260305-r
С момента выхода книги скорее всего появились новые версии JGit, проверьте обновления на https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit. После обновления конфигурации Maven автоматически скачает JGit нужной версии и добавит её к проекту.
Если вы управляете зависимостями вручную, собранные бинарные пакеты JGit доступны на https://www.eclipse.org/jgit/download. Использовать их в своём проекте можно следующим способом:
javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App
Служебный API
У JGit есть два уровня API: служебный («plumbing» API, «трубопровод») и пользовательский («porcelain» API, «фарфор»). Эта терминология заимствована из самого Git и JGit разделён на две части: «фарфоровый» API предоставляет удобные методы для распространённых задач прикладного уровня (тех, для решения которых вы бы использовали обычные Git-команды) и «сантехнический» API для прямого взаимодействия с низкоуровневыми объектами репозитория.
Начальная точка большинства сценариев использования JGit — класс Repository и первое, что необходимо сделать — это создать объект данного класса. Для репозиториев основанных на файловой системе (да, JGit позволяет использовать другие модели хранения) эта задача решается с помощью класса FileRepositoryBuilder :
// Создание нового репозитория; каталог должен существовать Repository newlyCreatedRepo = FileRepositoryBuilder.create( new File("/tmp/new_repo/.git")); newlyCreatedRepo.create(); // Открыть существующий репозиторий Repository existingRepo = new FileRepositoryBuilder() .setGitDir(new File("my_repo/.git")) .build();
Вызовы методов билдера можно объединять в цепочку чтобы указать всю информацию для поиска репозитория независимо от того, знает ли ваша программа его точное месторасположение или нет. Можно читать системные переменные ( .readEnvironment() ), начать поиск с произвольного места в рабочем каталоге ( .setWorkTree(…).findGitDir() ), или просто открыть каталог .git по указанному пути.
После создания объекта типа Repository , вам будет доступен широкий набор операций над ним. Краткий пример:
// Получение ссылки Ref master = repo.getRef("master"); // Получение объекта, на который она указывает ObjectId masterTip = master.getObjectId(); // Использование синтаксиса rev-parse ObjectId obj = repo.resolve("HEAD^"); // Получение «сырых» данных ObjectLoader loader = repo.open(masterTip); loader.copyTo(System.out); // Создание ветки RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1"); createBranch1.setNewObjectId(masterTip); createBranch1.update(); // Удаление ветки RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1"); deleteBranch1.setForceUpdate(true); deleteBranch1.delete(); // Работа с конфигурацией Config cfg = repo.getConfig(); String name = cfg.getString("user", null, "name");
Тут происходит много интересного, давайте разберёмся по порядку.
Первая строка получает указатель на ссылку master . JGit автоматически получает актуальную информацию о master , хранимую по пути refs/heads/master , и возвращает объект, предоставляющий доступ к информации о ссылке. Вы можете получить имя ( .getName() ), а также целевой объект прямой ссылки ( .getObjectId() ) или ссылку, на которую указывает другая символьная ссылка ( .getTarget() ). Объекты типа Ref также служат для представления ссылок на теги и самих тегов; вы можете узнать, является ли тег «конечным» («peeled»), т. е. ссылается ли он на целевой объект потенциально длинной цепи тегов.
Вторая строка получает объект на который указывает ссылка master в виде ObjectId. ObjectId представляют SHA-1-хеш объекта, который, возможно, сохранён внутри базы данных объектов Git. Следующая строка похожа на предыдущую, но используется синтаксис rev-parse (см. детали в разделе Ссылки на ветки главы 7); вы можете использовать любой, подходящий формат и JGit вернёт либо валидный ObjectId для указанного объекта, либо null .
Следующие две строки показывают, как можно получить содержимое объекта. В этом примере мы используем ObjectLoader.copyTo() чтобы передать содержимое файла прямиком в stdout, но у ObjectLoader есть методы для чтения типа и размера объекта, а также для считывания объекта в виде массива байтов. Для больших объектов (у которых .isLarge() возвращает true ) можно использовать метод .openStream() для открытия потока последовательного чтения объекта без полной загрузки в память.
Следующие строки показывают, как создать новую ветку. Мы создаём объект типа RefUpdate, устанавливаем некоторые параметры и вызываем метод .update() чтобы инициировать изменение. После этого мы удаляем эту же ветку. Обратите внимание на необходимость вызова .setForceUpdate(true) для корректной работы; иначе вызов .delete() вернёт REJECTED и ничего не произойдёт.
Последний кусок кода показывает как получить параметр user.name из файлов конфигурации Git. Созданный объект Config будет использовать открытый ранее репозиторий для чтения локальной конфигурации, также он автоматически находит файлы глобальной и системной конфигурации и использует их для чтения значений.
Это лишь малая часть служебного API JGit; в вашем распоряжении окажется гораздо больше классов и методов. Мы не показали как JGit обрабатывает ошибки. JGit использует механизм исключений Java; иногда он бросает стандартные исключения (типа IOException ), иногда — специфичные для JGit (например NoRemoteRepositoryException , CorruptObjectException и NoMergeBaseException ).
Пользовательский API
Служебные API достаточно всеобъемлющи, но сложны в использовании для простых задач вроде добавления файла в индекс или создания нового коммита. У JGit есть API более высокого уровня, входная точка в который — это класс Git :
Repository repo; // создание репозитория. Git git = new Git(repo);
В классе Git можно найти отличный набор высокоуровневых «текучих» методов (builder-style / fluent interface). Давайте взглянем на пример — результат выполнения этого кода напоминает git ls-remote :
CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd"); Collection remoteRefs = git.lsRemote() .setCredentialsProvider(cp) .setRemote("origin") .setTags(true) .setHeads(false) .call(); for (Ref ref : remoteRefs) < System.out.println(ref.getName() + " ->" + ref.getObjectId().name()); >
Тут показан частый случай использования класса Git: методы возвращают тот же объект, на котором вызваны, что позволяет чередовать их друг за другом, устанавливая параметры, а выполнение происходит при вызове .call() . В этом примере мы запрашиваем с удалённого репозитория origin список тегов, исключая ветки. Обратите внимание на использование класса CredentialsProvider для аутентификации.
Множество команд доступно в классе Git, включая такие как add , blame , commit , clean , push , rebase , revert , reset и другие.
Дополнительные материалы
Это лишь небольшой пример всех возможностей JGit. Если вы заинтересованы в более детальной работе с JGit, вот список источников информации для старта:
- Официальная документация по JGit API доступна в Интернете на https://www.eclipse.org/jgit/documentation. Это обыкновенный Javadoc, так что ваша любимая IDE может скачать её и использовать оффлайн.
- «Поваренная книга» JGit, расположенная по адресу https://github.com/centic9/jgit-cookbook, включает в себя много готовых рецептов использования JGit для решения тех или иных задач.
Круги ада с GitHub Actions (строим CI/CD pipeline для Java-проекта)

Мне частенько приходится строить пайплайн для сборки проектов на Java. Иногда это опенсорс, иногда нет. Недавно я решил попробовать перенести часть своих репозиториев с Travis-CI и TeamCity на GitHub Actions, и вот что из этого получилось.
Что будем автоматизировать
Для начала нам нужен проект, который мы будем автоматизировать, давайте сделаем небольшое приложение на Spring boot / Java 11 / Maven. В рамках этой статьи логика приложения нас интересовать не будет совсем, нам важна инфраструктура вокруг приложения, так что нам хватит простенького REST API контроллера.
Посмотреть исходники можно тут: github.com/antkorwin/github-actions все этапы построения pipeline-конвейера отражены в пулл-реквестах этого проекта.
JIRA и планирование
Стоит сказать, что мы обычно используем JIRA в качестве трекера задач, так что давайте заведем отдельную борду под этот проект и накидаем туда первые задачи:

Чуть позже мы еще вернемся к тому, что интересного могут дать в связке JIRA и GitHub.
Автоматизируем сборку проекта
Наш тестовый проект собирается через maven, так что сборка его довольно простая, все, что нам нужно, это mvn clean package.
Чтобы сделать это при помощи Github Actions, нам нужно будет создать в репозитории файл с описанием нашего workflow, это можно сделать обычным yml-файлом, не могу сказать что мне нравится «программирование на yml», но что поделать — делаем в директории .github/workflow/ файл build.yml в котором будем описывать действия при сборке мастер ветки:
name: Build on: pull_request: branches: - '*' push: branches: - 'master' jobs: build: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 - name: set up JDK 11 uses: actions/setup-java@v1 with: java-version: 1.11 - name: Maven Package run: mvn -B clean package -DskipTests
on — это описание события, по которому будет запускаться наш скрипт.
on: pull_request / push — говорит о том, что этот workflow нужно запускать при каждом пуше в мастер и создании пулл-реквестов.
Дальше идет описание заданий (jobs) и шаги выполнения (steps) для каждой задачи.
runs-on — тут мы можем выбрать целевую ОС, на удивление можно выбрать даже Mac OS, но на приватных репозиториях это довольно дорогое удовольствие (в сравнении с linux).
uses позволяет переиспользовать другие экшены, так например при помощи экшена actions/setup-java мы устанавливаем окружение для Java 11.
При помощи with мы можем указать параметры с которыми запускаем действие, по сути это аргументы, которые будут передаваться в экшен.
Остается только запустить мавеном сборку проекта: run: mvn -B clean package флаг -B говорит о том, что нам нужен non-interactive mode, чтобы мавен вдруг не захотел что-то у нас спросить

Отлично! Теперь при каждом коммите в мастер, запускается сборка проекта.
Автоматизируем запуск тестов
Сборка это хорошо, но в реальности проект может благополучно собираться, но не работать. Поэтому следующим шагом нужно заняться автоматизацией прогона тестов. К тому же, довольно удобно смотреть результат прохода тестов, когда делаешь ревью PR — ты точно знаешь, что тесты проходят и никто не забыл, перед тем как делать merge, прогнать свою ветку.
Делаем запуск тестов при создании пулл-реквеста и merge в мастер, а заодно добавим построение отчета о code-coverage.
name: Build on: pull_request: branches: - '*' push: branches: - 'master' jobs: build: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v1 - name: set up JDK 11 uses: actions/setup-java@v1 with: java-version: 1.11 - name: Maven Verify run: mvn -B clean verify - name: Test Coverage uses: codecov/codecov-action@v1 with: token: $>
Для покрытия тестов я использую codecov в связке с jacoco плагином. У codecov есть свой экшен, но ему для работы с нашим pull-request-ом нужен токен:
$> — такую конструкцию мы будем встречать еще не один раз, secrets это механизм хранения секретов в гитхабе, мы можем там прописать пароли/токены/хосты/url-ы и прочие данные, которыми не стоит светить в кодовой базе репозитория.
Добавить переменную в secrets, можно в настройках репозитория на GitHub:

Получить токен можно на codecov.io после авторизации через GitHub, для добавления public проекта нужно просто пройти по ссылке вида: GitHub user name/[repo name]. Приватный репозиторий тоже можно добавить, для этого надо дать права codecov приложению в гитхабе.

Добавляем jacoco плагин в POM-файл:
org.jacoco jacoco-maven-plugin 0.8.4 prepare-agent report test report org.apache.maven.plugins maven-surefire-plugin 2.22.2 plain **/*Test*.java **/*IT*.java
Теперь в каждый наш пулл-реквест будет заходить codecov бот и добавлять график изменения покрытия:

Добавим статический анализатор
В большинстве своих oпенсорс-проектов я использую sonar cloud для статического анализа кода, его довольно легко подключить к travis-ci. Так что это логичный шаг при миграции на GitHub Actions, сделать тоже самое. Маркет экшенов — клевая штука, но в этот раз он немного подвел, потому что я по привычке нашел нужный экшен и прописал его в workflow. А оказалось, что sonar не поддерживает работу через действие для анализа проектов на maven или gradle. Об этом конечно написано в документации, но кто же ее читает?!
Через действие нельзя, поэтому будем делать через mvn плагин:
name: SonarCloud on: push: branches: - master pull_request: types: [opened, synchronize, reopened] jobs: sonarcloud: runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v1 - name: Set up JDK uses: actions/setup-java@v1 with: java-version: 1.11 - name: Analyze with SonarCloud # set environment variables: env: GITHUB_TOKEN: $> SONAR_TOKEN: $> # run sonar maven plugin: run: mvn -B verify sonar:sonar -Dsonar.projectKey=antkorwin_github-actions -Dsonar.organization=antkorwin-github -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=$SONAR_TOKEN -Dsonar.coverage.jacoco.xmlReportPaths=./target/site/jacoco/jacoco.xml
SONAR_TOKEN — можно получить в sonarcloud.io и нужно прописать его в secrets. GITHUB_TOKEN — это встроенный токен, который генерит гитхаб, с помощью него sonarcloud[bot] сможет авторизоваться в гите, чтобы оставлять нам сообщения в пулл-реквестах.
Dsonar.projectKey — название проекта в сонаре, посмотреть можно в настройках проекта.
Dsonar.organization — название организации из GitHub.
Делаем пулл-реквест и ждем, когда sonarcloud[bot] придет в комментарии:

Release management
Билд настроили, тесты прогнали, можно и релиз сделать. Давайте посмотрим, как GitHub Actions помогает существенно упростить release managеment.
На работе у меня есть проекты, кодовая база которых лежит в bitbucket(все как в той истории «днем пишу в битбакет, ночью коммичу в GitHub»). К сожалению, в bitbucket нет встроенных средств для управления релизами. Это проблема, потому что под каждый релиз приходится руками заводить страничку в confluence и скидывать туда все фичи вошедшие в релиз, шерстить чертоги разума, таски в jira, коммиты в репозитории. Шансов ошибиться много, можно что-то забыть или вписать то, что уже релизили в прошлый раз, иногда просто не понятно, к чему отнести какой-то пулл-реквест — это фича или фикс багов, или правка тестов, или что-то инфраструктурное.
Как нам может помочь GitHub actions? Есть отличный экшен — release drafter, он позволяет задать шаблон файла release notes, чтобы настроить категории пулл-реквестов и автоматически группировать их в release notes файле:

Пример шаблона для настройки отчета(.github/release-drafter.yml):
name-template: 'v$NEXT_PATCH_VERSION' tag-template: 'v$NEXT_PATCH_VERSION' categories: - title: ' New Features' labels: - 'type:features' # в эту категорию собираем все PR с меткой type:features - title: ' Bugs Fixes' labels: - 'type:fix' # аналогично для метки type:fix и т.д. - title: ' Documentation' labels: - 'type:documentation' - title: ' Configuration' labels: - 'type:config' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' template: | ## Changes $CHANGES
Добавляем скрипт для генерации черновика релиза (.github/workflows/release-draft.yml):
name: "Create draft release" on: push: branches: - master jobs: update_draft_release: runs-on: ubuntu-18.04 steps: - uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: $>
Все пулл-реквесты с этого момента будут собираться в release notes автоматически — magic!
Тут может возникнуть вопрос: а что если разработчики забудут проставить метки в PR? Тогда непонятно, в какую категорию его отнести, и опять придется разбираться вручную, с каждым PR-ом отдельно. Чтобы исправить эту проблему, мы можем воспользоваться еще одним экшеном — label verifier — он проверяет наличие тэгов на пулл-реквесте. Если нет ни одного обязательного тэга, то проверка будет завалена и сообщение об этом мы увидим в нашем пулл-реквесте.
name: "Verify type labels" on: pull_request: types: [opened, labeled, unlabeled, synchronize] jobs: triage: runs-on: ubuntu-18.04 steps: - uses: zwaldowski/match-label-action@v2 with: allowed: 'type:fix, type:features, type:documentation, type:tests, type:config'
Теперь любой pull-request нужно пометить одним из тэгов: type:fix, type:features, type:documentation, type:tests, type:config.

Авто-аннотирование пулл-реквестов
Раз уж мы коснулись такой темы как эффективная работа с пулл-реквестами, то стоит сказать еще о таком экшене, как labeler, он проставляет метки в PR на основании того, какие файлы были изменены. Например, мы можем пометить как [build] любой пул-реквест в котором есть изменения в каталоге .github/workflow .
Подключить его довольно просто:
name: "Auto-assign themes to PR" on: - pull_request jobs: triage: runs-on: ubuntu-18.04 steps: - uses: actions/labeler@v2 with: repo-token: $>
Еще нам понадобится файл с описанием соответствия каталогов проекта с тематиками пулл-реквестов:
theme:build: - ".github/**" - "pom.xml" - ".travis.yml" - ".gitignore" - "Dockerfile" theme:code: - "src/main/*" theme:tests: - "src/test/*" theme:documentation: - "docs/**" theme:TRASH: - ".idea/**" - "target/**"
Подружить действие автоматически проставляющее метки в пулл-реквесты и действие, проверяющее наличие обязательных меток, у меня не вышло, match-label на отрез не хочет видеть проставленные ботом метки. Похоже проще написать свое действие, совмещающее оба этапа. Но даже в таком виде пользоваться довольно удобно, нужно выбрать метку из списка при создании пулл-реквеста.
Пора деплоить

Я попробовал несколько вариантов деплоя через GitHub Actions (через ssh, через scp, и при помощи docker-hub), и могу сказать, что, скорее всего, вы найдете способ залить бинарку на сервер, каким бы извращенным не был ваш pipeline.
Мне понравился вариант держать всю инфраструктуру в одном месте, поэтому рассмотрим, как сделать деплой в GitHub Packages (это репозиторий для бинарного контента, npm, jar, docker).
Cкприпт сборки docker образа и публикации его в GitHub Packages:
name: Deploy docker image on: push: branches: - 'master' jobs: build_docker_image: runs-on: ubuntu-18.04 steps: # Build JAR: - uses: actions/checkout@v1 - name: set up JDK 11 uses: actions/setup-java@v1 with: java-version: 1.11 - name: Maven Package run: mvn -B clean compile package -DskipTests # Set global environment variables: - name: set global env id: global_env run: | echo "::set-output name=IMAGE_NAME::$" echo "::set-output name=DOCKERHUB_IMAGE_NAME::docker.pkg.github.com/$/$" # Build Docker image: - name: Build and tag image run: | docker build -t "$>:latest" -t "$>:$" . - name: Docker login run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $> # Publish image to github package repository: - name: Publish image env: IMAGE_NAME: $GITHUB_REPOSITORY run: docker push "docker.pkg.github.com/$GITHUB_REPOSITORY/$>"
Для начала нам надо собрать JAR-файл нашего приложения, после чего мы вычисляем путь к GitHub docker registry и название нашего образа. Тут есть несколько хитростей, с которыми мы еще не сталкивались:
- конструкция вида: echo «::set-output name=NAME::VALUE» позволяет задать значение переменной в текущем шаге, так чтобы его потом можно было прочитать во всех остальных шагах.
- получить значение переменной установленой на предыдущем шаге можно через идентификатор этого шага: $>
- В стандартной переменной GITHUB_REPOSITORY хранится название репозитория и его владелец («owner/repo-name»). Для того чтобы вырезать из этой строки все кроме названия репозитория, воспользуемся bash синтаксисом: $
Далее нам нужно собрать докер-образ:
docker build -t «docker.pkg.github.com/antkorwin/github-actions/github-actions:latest»
Авторизоваться в registry:
docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $>
И опубликовать образ в GitHub Packages Repository:
docker push «docker.pkg.github.com/antkorwin/github-actions/github-actions»
Для того чтобы указать версию образа, мы используем первые цифры из SHA-хэша коммита — GITHUB_SHA тут тоже есть нюансы, если вы будете делать такие сборки не только при merge в master, а еще и по событию создания пулл-реквеста, то SHA может не совпадать с хэшем, который мы видим в истории гита, потому что действие actions/checkout делает свой уникальный хэш, чтобы избежать взаимных блокировок действий в PR.

Если все получилось благополучно, то открыв раздел packages (https://github.com/antkorwin/github-actions/packages) в репозитории, вы увидите новый докер образ:

Там же можно посмотреть список версий докер-образа.
Остается только настроить наш сервер на работу с этим registry и запустить перезапуск сервиса. О том как это сделать через systemd, я, пожалуй, расскажу в другой раз.
Мониторинг
Давайте посмотрим несложный вариант, как делать health check нашего приложения при помощи GitHub Actions. В нашем бутовом приложении есть actuator, так что API для проверки его состояния даже и писать не надо, для ленивых уже все сделали. Нужно только дернуть хост: SERVER-URL:PORT/actuator/health
$ curl -v 127.0.0.1:8080/actuator/health > GET /actuator/health HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.61.1 > Accept: */* < HTTP/1.1 200 < Content-Type: application/vnd.spring-boot.actuator.v3+json
Все, что нам нужно — написать таск проверки сервера по крону, ну а если вдруг он нам не ответит, то будем слать уведомление в телеграм.
Для начала разберемся, как запустить workflow по крону:
on: schedule: - cron: '*/5 * * * *'
Все просто, даже не верится что в гитхабе можно сделать такие ивенты, которые совсем не укладываются в webhook-и. Детали есть в документации: help.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule
Проверку статуса сервера сделаем руками через curl:
jobs: ping: runs-on: ubuntu-18.04 steps: - name: curl actuator id: ping run: | echo "::set-output name=status::$(curl $>/api/actuator/health)" - name: health check run: | if [[ $> != *"UP"* ]]; then echo "health check is failed" exit 1 fi echo "It's OK"
Сначала сохраняем в переменную то, что ответил сервер на запрос, на следующем шаге проверяем что статус UP и, если это не так, то выходим с ошибкой. Если нужно руками «завалить» действие, то exit 1 — подходящее оружие.
- name: send alert in telegram if: $> uses: appleboy/telegram-action@master with: to: $> token: $> message: | Health check of the: $>/api/actuator/health failed with the result: $>
Отправку в телеграм делаем, только если действие завалилось на предыдущем шаге. Для отправки сообщения используем appleboy/telegram-action, о том, как получить токен бота и id чата можно почитать в документации: github.com/appleboy/telegram-action

Не забудьте прописать в секретах на гитхабе: URL для сервера и токены для телеграм бота.
Бонус трек — JIRA для ленивых
Я обещал что мы вернемся к JIRA, и мы вернулись. Сотни раз наблюдал на стендапах ситуацию, когда разработчики сделали фичу, слили ветку, но забыли перетянуть задачу в JIRA. Конечно, если бы все это делалось в одном месте, то было бы проще, но фактически мы пишем код в IDE, сливаем ветки в bitbucket или GitHub, а задачи потом таскаем в Jira, для этого надо открывать новые окна, иногда логиниться еще раз и т.д. Когда ты прекрасно помнишь, что надо делать дальше, то открывать борду лишний раз нет смысла. В итоге, утром на стендапе надо тратить время на актуализацию доски задач.
GitHub поможет нам и в этом рутинном занятии, для начала мы можем перетягивать задачи автоматом, в колонку code_review, когда закинули пулл-реквест. Все, что нужно — это придерживаться соглашения в наименовании веток:
[имя проекта]-[номер таска]-название
например, если ключ проекта «GitHub Actions» будет GA, то GA-8-jira-bot может быть веткой для реализации задачи GA-8.
Интеграция с JIRA работает через экшены от Atlassian, они не идеальны, надо сказать, что некоторые из них у меня вообще не заработали. Но мы обсудим только те, что точно работают и активно используются.
Для начала нужно пройти авторизацию в JIRA при помощи действия: atlassian/gajira-login
jobs: build: runs-on: ubuntu-latest name: Jira Workflow steps: - name: Login uses: atlassian/gajira-login@master env: JIRA_BASE_URL: $> JIRA_USER_EMAIL: $> JIRA_API_TOKEN: $>
Для этого надо получить токен в JIRA, как это сделать расписано тут: confluence.atlassian.com/cloud/api-tokens-938839638.html
Вычленяем идентификатор задачи из названия ветки:
- name: Find Issue id: find_issue shell: bash run: | echo "::set-output name=ISSUE_ID::$(echo $ | egrep -o 'GA-[0-9]')" echo brach name: $GITHUB_HEAD_REF echo extracted issue: $ | egrep -o 'GA-[0-9]' - name: Check Issue shell: bash run: | if [[ "$>" == "" ]]; then echo "Please name your branch according to the JIRA issue: [project_key]-[task_number]-branch_name" exit 1 fi echo succcessfully found JIRA issue: $>
Если поискать в GitHub marketplace, то можно найти действие для этой задачи, но мне пришлось написать тоже самое через grep по названию ветки, потому что это действие от Atlassian ни в какую не захотело работать на моем проекте, разбираться, что же там не так — дольше, чем сделать руками тоже самое.
Осталось только переместить задачу в колонку «Code review» при создании пулл-реквеста:
- name: Transition issue if: $> uses: atlassian/gajira-transition@master with: issue: $> transition: "Code review"
Для этого есть специальное действие на GitHub, все, что ему нужно — это идентификатор задачи, полученный на предыдущем шаге и авторизация в JIRA, которую мы делали выше.
Таким же образом можно перетягивать задачи при merge в мастер, и других событиях из GitHub workflow. В общем, все зависит от вашей фантазии и желания автоматизировать рутинные процессы.
Выводы
Если посмотреть на классическую диаграмму DEVOPS, то мы покрыли все этапы, разве что кроме operate, думаю, если постараться, то можно найти какой-нибудь экшен в маркете для интеграции с help-desk системой, так что будем считать что pipeline получился основательный и на основании его использования можно сделать выводы.

- Marketplace с готовыми действиями на все случаи жизни, это очень круто. В большинстве из них еще и исходники можно посмотреть, чтобы понять как решить похожую задачу либо запостить feature request автору прямо в гитхаб репозитории.
- Выбор целевой платформы для сборки: Linux, mac os, windows довольно интересная фича.
- Github Packages отличная вещь, держать всю инфраструктуру в одном месте удобно, не надо серфить по разным окошкам, все в радиусе одного-двух кликов мыши и прекрасно интегрировано с GitHub Actions. Поддержка docker registry в бесплатной версии — это тоже хорошее преимущество.
- GitHub прячет секреты в логах сборки, поэтому пользоваться им для хранения паролей и токенов не так уж и страшно. За все время экспериментов мне не удалось ни разу увидеть секрет в чистом виде в консоли.
- Бесплатен для Open Source проектов
- YML, ну не люблю я его. При работе с таким флоу у меня самый частый commit message это «fix yml format», то забудешь где-то таб поставить, то не на той строке напишешь. В общем, сидеть перед экраном с транспортиром и линейкой не самое приятное занятие.
- DEBUG, отлаживать флоу коммитами, запуском пересборки и выводом в консоль не всегда удобно, но это больше из разряда «вы зажрались», привыкли работать с удобными IDEA, когда можно отлаживать все, что угодно.
- Свой экшен можно написать на чем угодно если завернуть его в докер, но нативно поддерживается только javascript, конечно это дело вкуса, но я бы предпочел что-то другое заместо js.
На следующей неделе я буду выступать с докладом на конференции Heisenbug 2020 Piter. Расскажу не только, как избежать ошибок при подготовке тестовых данных, но и поделюсь своими секретами работы с наборами данных в Java-приложениях!
