- Регистрация
- 12.04.17
- Сообщения
- 19.095
- Реакции
- 107
- Репутация
- 0
В данной статье я хочу описать свой опыт по доставке Graal Native Image конечным пользователям Mac OS и Linux в одну команду при помощи Travis CI.
Когда я столкнулся с данной задачей, я ощутил нехватку информации по этой теме, поэтому я решил описать свой опыт здесь.
Пожалуй начнем.
У меня есть:
Что хочу получить:
Поехали
Для начала создадим простой Hello world.
package release.test;
public class Application {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
А также добавим базовые плагины в pom.xml.
org.apache.maven.plugins
maven-compiler-plugin
3.8.0
org.apache.maven.plugins
maven-jar-plugin
3.1.0
true
lib/
release.test.Application
org.apache.maven.plugins
maven-release-plugin
2.5.3
${project.version}
Дальше я создам два профиля для сборки mac и linux соответственно. Буду использовать maven-assembly-plugin. Я не буду засорять статью тоннами xml, вместо этого я прикреплю
Перейдем к Travis CI. Именно там я хочу собрать native image, упаковать его в нужные пакеты и задеплоить.
Создадим файл .travis.yml для описания деплой плана и будем постепенно его наполнять.
Для начала он будет выглядеть так:
os:
- linux
- osx
language: java
install: true
script:
- if [ $TRAVIS_OS_NAME == "osx" ]; then
chmod +x ./prepare-mac.sh;
./prepare-mac.sh;
fi
- if [ $TRAVIS_OS_NAME == "linux" ]; then
chmod +x ./prepare-linux.sh;
./prepare-linux.sh;
fi
Как видно выше, будем использовать две операционные системы. Для каждой из них создадим по скрипту для подготовки всего необходимого.
Для Mac OS скрипт будет выглядеть так:
#!/usr/bin/env bash
curl -OL
tar zxf graalvm-ce-darwin-amd64-19.1.1.tar.gz
sudo mv graalvm-ce-19.1.1 /Library/Java/JavaVirtualMachines
export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin:$PATH
/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin/gu install native-image
mvn clean package -P mac
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
native-image -jar target/${NAME} -H:Name=release-test
mvn package -P assembly
Пройдемся по шагам. Грузим, распаковываем и устанавливаем GraalVM, а также догружаем native-image, так как его просто нет в сборке Graal для Mac. После этого запускаем сборку проекта, чтобы получить jar, а далее запускаем native-image и снова упаковываем все в архив, в котором уже будет лежать наш исполняемый файл.
Для Linux скрипт будет выглядеть немного иначе, так как в нем мы сразу соберем rpm и deb пакеты:
#!/usr/bin/env bash
curl -OL
tar zxf graalvm-ce-linux-amd64-19.1.1.tar.gz
sudo mv graalvm-ce-19.1.1 /usr/lib/jvm/
export JAVA_HOME=/usr/lib/jvm/graalvm-ce-19.1.1
export PATH=$PATH:${JAVA_HOME}/bin
mvn clean install
${JAVA_HOME}/bin/gu install native-image
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
mkdir release-deb
cd release-deb
native-image -jar ../target/${NAME} -H:Name=release-test
PACK_NAME=$(ls)
chmod +x ${PACK_NAME}
mkdir packageroot
mkdir packageroot/DEBIAN
touch packageroot/DEBIAN/control
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
echo "Package: $PACK_NAME
Version: $VERSION
Architecture: amd64
Maintainer: John Doe
Description: test
" > packageroot/DEBIAN/control
cat packageroot/DEBIAN/control
mkdir -p packageroot/usr/bin
cp ${PACK_NAME} packageroot/usr/bin/
dpkg-deb -b packageroot ${PACK_NAME}-${VERSION}.deb
sudo dpkg -i ./${PACK_NAME}-${VERSION}.deb
sudo apt-get install -f
DEB_PACK=$(find . -type f -name 'release-test-*.deb')
echo ${DEB_PACK}
cp ${DEB_PACK} ../target/
cd ../target
# convert to rpm
sudo apt-get update
sudo apt-get install rpm
sudo apt-get install ruby ruby-dev rubygems build-essential
gem install --no-ri --no-rdoc fpm
fpm -t rpm -s deb ${PACK_NAME}-${VERSION}.deb
Здесь также грузим GraalVM и устанавливаем native-image. Далее каждый участок кода, я думаю, имеет смысл рассмотреть в отдельности.
Создаем debian пакет. В интернете есть информация, каким образом создаются пакеты, поэтому я не буду углубляться в подробности.
mkdir release-deb
cd release-deb
native-image -jar ../target/${NAME} -H:Name=release-test
PACK_NAME=$(ls)
chmod +x ${PACK_NAME}
mkdir packageroot
mkdir packageroot/DEBIAN
touch packageroot/DEBIAN/control
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
echo "Package: $PACK_NAME
Version: $VERSION
Architecture: amd64
Maintainer: John Doe
Description: test
" > packageroot/DEBIAN/control
cat packageroot/DEBIAN/control
mkdir -p packageroot/usr/bin
cp ${PACK_NAME} packageroot/usr/bin/
dpkg-deb -b packageroot ${PACK_NAME}-${VERSION}.deb
sudo dpkg -i ./${PACK_NAME}-${VERSION}.deb
sudo apt-get install -f
DEB_PACK=$(find . -type f -name 'release-test-*.deb')
echo ${DEB_PACK}
cp ${DEB_PACK} ../target/
Далее я хочу конвертировать debian пакет в rpm. Мне не хотелось прогонять все те же действия еще раз, поэтому я решил использовать
# convert to rpm
sudo apt-get update
sudo apt-get install rpm
sudo apt-get install ruby ruby-dev rubygems build-essential
gem install --no-ri --no-rdoc fpm
fpm -t rpm -s deb ${PACK_NAME}-${VERSION}.deb
Далее я добавлю небольшой before_deploy блок в .travis.yml, который будет обновлять служебные файлы.
before_deploy:
- if [ $TRAVIS_OS_NAME == "linux" ]; then
chmod +x ./update-version.sh;
./update-version.sh;
fi
update-version.sh
#!/usr/bin/env bash
cd target
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
cd ../
sed -i "s/template_version/$VERSION/g" deploy-deb.json
sed -i "s/template_tag/$VERSION/g" deploy-deb.json
sed -i "s/template_version/$VERSION/g" deploy-rpm.json
sed -i "s/template_tag/$VERSION/g" deploy-rpm.json
Файлы deploy-deb.json и deploy-rpm.json — это описание деплоя на bintray. Я также не буду добавлять их сюда. Пример можно найти в официальной
Переходим к этапу деплоя. Здесь мы будем загружать все пакеты в отдельные bintray репозитории. Для этого необходимо создать generic, rpm и debian репозитории на bintray. Для каждой системы у нас будет отдельный блок, релизить будем по тегу и только с master-ветки. В случае с mac воспользуемся Rest API. Rest API Bintray позволяет заливать архив прямо в репозиторий. Для linux сборок будем использовать bintray provider, который предоставляет Travis CI.
deploy:
- provider: script
skip_cleanup: true
script:
cd target;
curl -X PUT -H X-GPG-PASSPHRASE:$PASSPHRASE_BINTRAY --basic -u aarrsseni:$BINTRAY_API_KEY
on:
branch: master
condition: $TRAVIS_OS_NAME == "osx"
tags: true
- provider: bintray
file: deploy-deb.json
user: aarrsseni
key:
secure: your_key
passphrase:
secure: your_passphrase
skip_cleanup: true
on:
branch: master
condition: $TRAVIS_OS_NAME == "linux"
tags: true
- provider: bintray
file: deploy-rpm.json
user: aarrsseni
key:
secure: your_key
passphrase:
secure: your_passphrase
skip_cleanup: true
on:
branch: master
condition: $TRAVIS_OS_NAME == "linux"
tags: true
В результате у нас есть zip архив, rpm и deb пакеты, которые уже лежат на bintray.
В случае rpm и deb пользователь уже может загрузить их через apt-get и yum.
Для mac мы будем использовать brew в качестве пакетного менеджера. Для этого необходимо создать еще один
Основной файл для brew находится в папке Formula. Мой файл называется release-test.rb. Он содержит ссылку на zip архив на bintray, некоторое описание и чексумму, которую необходимо менять при каждом релизе.
class ReleaseTest < Formula
desc "Your desc"
homepage "your website"
url "
sha256 "84b09c9c1c45ef0b040811be58c9f14cf8ef7237139f0a8483ccd85c6ea1bb64"
bottle :unneeded
def install
libexec.install Dir["*"]
bin.install_symlink libexec/"bin/release-test"
end
test do
system "release-test"
end
end
Чтобы не менять чексумму руками, в .travis.yml добавляем финальный блок after_deploy.
after_deploy:
- if [ $TRAVIS_OS_NAME == "osx" ]; then
chmod +x ./update-homebrew.sh;
./update-homebrew.sh;
fi
В скрипте update-homebrew.sh мы клонируем brew репозиторий, вычисляем новую чексумму для только задеплоенного приложения, а затем обновляем ее.
#!/usr/bin/env bash
NAME=$(basename $(find . -type f -name 'release-test-*.zip'))
echo ${NAME}
mkdir temp-homebrew
CHECKSUM=$(echo "$(shasum -a 256 target/${NAME})" | awk '{print $1;}')
echo ${CHECKSUM}
cd temp-homebrew
git clone
cd homebrew-test-release
git remote add origin-deploy https://${GITHUB_TOKEN}@github.com/aarrsseni/homebrew-test-release.git
cd Formula
cat release-test.rb
PREV_NAME=$(grep -o 'file_path=.*$' release-test.rb | cut -c11-)
PREV_NAME=${PREV_NAME%?}
PREV_CHECKSUM_FROM_FILE=$(grep -o 'sha256.*$' release-test.rb | cut -c9-)
PREV_CHECKSUM=${PREV_CHECKSUM_FROM_FILE%?}
echo ${PREV_NAME}
echo ${PREV_CHECKSUM}
sed -i '.bak' "s/$PREV_NAME/$NAME/g" release-test.rb
sed -i '.bak' "s/$PREV_CHECKSUM/$CHECKSUM/g" release-test.rb
cat release-test.rb
git add .
git commit -m "Update formula version"
git push origin-deploy master
После этого, чтобы скачать и установить к себе на mac готовое приложение, необходимо запустить команду brew install USERNAME/REPO_NAME/TOOL_NAME, где USERNAME — имя на github, REPO_NAME — соответственно репозиторий, а TOOL_NAME — название вашего приложения.
Стоит добавить, что для доступа на github и bintray в .travis.yml надо добавить ваши ключи.
Подведем итог
Теперь по команде mvn release
repare или просто по коммиту с тегом мы стартуем сборку native image для mac и linux, а также доставку прямиком к конечному пользователю.
Когда я столкнулся с данной задачей, я ощутил нехватку информации по этой теме, поэтому я решил описать свой опыт здесь.
Пожалуй начнем.
У меня есть:
- Java приложение со всеми конфигурациями для сборки native image.
Что хочу получить:
- Собрать native image при помощи Travis
- Собрать rpm и deb дистрибутивы, а также архив для mac версии
- Залить полученные пакеты на Bintray
- Дать пользователям возможность загрузить приложение через пакетный менеджер
- Максимально автоматизировать процесс.
Поехали
Для начала создадим простой Hello world.
package release.test;
public class Application {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
А также добавим базовые плагины в pom.xml.
org.apache.maven.plugins
maven-compiler-plugin
3.8.0
org.apache.maven.plugins
maven-jar-plugin
3.1.0
true
lib/
release.test.Application
org.apache.maven.plugins
maven-release-plugin
2.5.3
${project.version}
Дальше я создам два профиля для сборки mac и linux соответственно. Буду использовать maven-assembly-plugin. Я не буду засорять статью тоннами xml, вместо этого я прикреплю
You must be registered for see links
с полным примером.Перейдем к Travis CI. Именно там я хочу собрать native image, упаковать его в нужные пакеты и задеплоить.
Создадим файл .travis.yml для описания деплой плана и будем постепенно его наполнять.
Для начала он будет выглядеть так:
os:
- linux
- osx
language: java
install: true
script:
- if [ $TRAVIS_OS_NAME == "osx" ]; then
chmod +x ./prepare-mac.sh;
./prepare-mac.sh;
fi
- if [ $TRAVIS_OS_NAME == "linux" ]; then
chmod +x ./prepare-linux.sh;
./prepare-linux.sh;
fi
Как видно выше, будем использовать две операционные системы. Для каждой из них создадим по скрипту для подготовки всего необходимого.
Для Mac OS скрипт будет выглядеть так:
#!/usr/bin/env bash
curl -OL
You must be registered for see links
tar zxf graalvm-ce-darwin-amd64-19.1.1.tar.gz
sudo mv graalvm-ce-19.1.1 /Library/Java/JavaVirtualMachines
export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin:$PATH
/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin/gu install native-image
mvn clean package -P mac
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
native-image -jar target/${NAME} -H:Name=release-test
mvn package -P assembly
Пройдемся по шагам. Грузим, распаковываем и устанавливаем GraalVM, а также догружаем native-image, так как его просто нет в сборке Graal для Mac. После этого запускаем сборку проекта, чтобы получить jar, а далее запускаем native-image и снова упаковываем все в архив, в котором уже будет лежать наш исполняемый файл.
Для Linux скрипт будет выглядеть немного иначе, так как в нем мы сразу соберем rpm и deb пакеты:
#!/usr/bin/env bash
curl -OL
You must be registered for see links
tar zxf graalvm-ce-linux-amd64-19.1.1.tar.gz
sudo mv graalvm-ce-19.1.1 /usr/lib/jvm/
export JAVA_HOME=/usr/lib/jvm/graalvm-ce-19.1.1
export PATH=$PATH:${JAVA_HOME}/bin
mvn clean install
${JAVA_HOME}/bin/gu install native-image
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
mkdir release-deb
cd release-deb
native-image -jar ../target/${NAME} -H:Name=release-test
PACK_NAME=$(ls)
chmod +x ${PACK_NAME}
mkdir packageroot
mkdir packageroot/DEBIAN
touch packageroot/DEBIAN/control
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
echo "Package: $PACK_NAME
Version: $VERSION
Architecture: amd64
Maintainer: John Doe
Description: test
" > packageroot/DEBIAN/control
cat packageroot/DEBIAN/control
mkdir -p packageroot/usr/bin
cp ${PACK_NAME} packageroot/usr/bin/
dpkg-deb -b packageroot ${PACK_NAME}-${VERSION}.deb
sudo dpkg -i ./${PACK_NAME}-${VERSION}.deb
sudo apt-get install -f
DEB_PACK=$(find . -type f -name 'release-test-*.deb')
echo ${DEB_PACK}
cp ${DEB_PACK} ../target/
cd ../target
# convert to rpm
sudo apt-get update
sudo apt-get install rpm
sudo apt-get install ruby ruby-dev rubygems build-essential
gem install --no-ri --no-rdoc fpm
fpm -t rpm -s deb ${PACK_NAME}-${VERSION}.deb
Здесь также грузим GraalVM и устанавливаем native-image. Далее каждый участок кода, я думаю, имеет смысл рассмотреть в отдельности.
Создаем debian пакет. В интернете есть информация, каким образом создаются пакеты, поэтому я не буду углубляться в подробности.
mkdir release-deb
cd release-deb
native-image -jar ../target/${NAME} -H:Name=release-test
PACK_NAME=$(ls)
chmod +x ${PACK_NAME}
mkdir packageroot
mkdir packageroot/DEBIAN
touch packageroot/DEBIAN/control
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
echo "Package: $PACK_NAME
Version: $VERSION
Architecture: amd64
Maintainer: John Doe
Description: test
" > packageroot/DEBIAN/control
cat packageroot/DEBIAN/control
mkdir -p packageroot/usr/bin
cp ${PACK_NAME} packageroot/usr/bin/
dpkg-deb -b packageroot ${PACK_NAME}-${VERSION}.deb
sudo dpkg -i ./${PACK_NAME}-${VERSION}.deb
sudo apt-get install -f
DEB_PACK=$(find . -type f -name 'release-test-*.deb')
echo ${DEB_PACK}
cp ${DEB_PACK} ../target/
Далее я хочу конвертировать debian пакет в rpm. Мне не хотелось прогонять все те же действия еще раз, поэтому я решил использовать
You must be registered for see links
. Данная тулза позволяет в одну команду создать rpm пакет из debian.# convert to rpm
sudo apt-get update
sudo apt-get install rpm
sudo apt-get install ruby ruby-dev rubygems build-essential
gem install --no-ri --no-rdoc fpm
fpm -t rpm -s deb ${PACK_NAME}-${VERSION}.deb
Далее я добавлю небольшой before_deploy блок в .travis.yml, который будет обновлять служебные файлы.
before_deploy:
- if [ $TRAVIS_OS_NAME == "linux" ]; then
chmod +x ./update-version.sh;
./update-version.sh;
fi
update-version.sh
#!/usr/bin/env bash
cd target
NAME=$(basename $(find . -type f -name 'release-test-*.jar'))
VERSION=$(echo "${NAME%.*}" | cut -d'-' -f 3)
cd ../
sed -i "s/template_version/$VERSION/g" deploy-deb.json
sed -i "s/template_tag/$VERSION/g" deploy-deb.json
sed -i "s/template_version/$VERSION/g" deploy-rpm.json
sed -i "s/template_tag/$VERSION/g" deploy-rpm.json
Файлы deploy-deb.json и deploy-rpm.json — это описание деплоя на bintray. Я также не буду добавлять их сюда. Пример можно найти в официальной
You must be registered for see links
, а также в примере в конце статьи. Скрипт update-version.sh исходя из названия всего лишь подменяет переменные в json файлах на актуальную версию.Переходим к этапу деплоя. Здесь мы будем загружать все пакеты в отдельные bintray репозитории. Для этого необходимо создать generic, rpm и debian репозитории на bintray. Для каждой системы у нас будет отдельный блок, релизить будем по тегу и только с master-ветки. В случае с mac воспользуемся Rest API. Rest API Bintray позволяет заливать архив прямо в репозиторий. Для linux сборок будем использовать bintray provider, который предоставляет Travis CI.
deploy:
- provider: script
skip_cleanup: true
script:
cd target;
curl -X PUT -H X-GPG-PASSPHRASE:$PASSPHRASE_BINTRAY --basic -u aarrsseni:$BINTRAY_API_KEY
You must be registered for see links
on:
branch: master
condition: $TRAVIS_OS_NAME == "osx"
tags: true
- provider: bintray
file: deploy-deb.json
user: aarrsseni
key:
secure: your_key
passphrase:
secure: your_passphrase
skip_cleanup: true
on:
branch: master
condition: $TRAVIS_OS_NAME == "linux"
tags: true
- provider: bintray
file: deploy-rpm.json
user: aarrsseni
key:
secure: your_key
passphrase:
secure: your_passphrase
skip_cleanup: true
on:
branch: master
condition: $TRAVIS_OS_NAME == "linux"
tags: true
В результате у нас есть zip архив, rpm и deb пакеты, которые уже лежат на bintray.
В случае rpm и deb пользователь уже может загрузить их через apt-get и yum.
Для mac мы будем использовать brew в качестве пакетного менеджера. Для этого необходимо создать еще один
You must be registered for see links
. На
You must be registered for see links
можно найти документацию, в которой описана последовательность действий по добавлению пакетов в менеджер. Как он выглядит можно посмотреть
You must be registered for see links
.Основной файл для brew находится в папке Formula. Мой файл называется release-test.rb. Он содержит ссылку на zip архив на bintray, некоторое описание и чексумму, которую необходимо менять при каждом релизе.
class ReleaseTest < Formula
desc "Your desc"
homepage "your website"
url "
You must be registered for see links
"sha256 "84b09c9c1c45ef0b040811be58c9f14cf8ef7237139f0a8483ccd85c6ea1bb64"
bottle :unneeded
def install
libexec.install Dir["*"]
bin.install_symlink libexec/"bin/release-test"
end
test do
system "release-test"
end
end
Чтобы не менять чексумму руками, в .travis.yml добавляем финальный блок after_deploy.
after_deploy:
- if [ $TRAVIS_OS_NAME == "osx" ]; then
chmod +x ./update-homebrew.sh;
./update-homebrew.sh;
fi
В скрипте update-homebrew.sh мы клонируем brew репозиторий, вычисляем новую чексумму для только задеплоенного приложения, а затем обновляем ее.
#!/usr/bin/env bash
NAME=$(basename $(find . -type f -name 'release-test-*.zip'))
echo ${NAME}
mkdir temp-homebrew
CHECKSUM=$(echo "$(shasum -a 256 target/${NAME})" | awk '{print $1;}')
echo ${CHECKSUM}
cd temp-homebrew
git clone
You must be registered for see links
cd homebrew-test-release
git remote add origin-deploy https://${GITHUB_TOKEN}@github.com/aarrsseni/homebrew-test-release.git
cd Formula
cat release-test.rb
PREV_NAME=$(grep -o 'file_path=.*$' release-test.rb | cut -c11-)
PREV_NAME=${PREV_NAME%?}
PREV_CHECKSUM_FROM_FILE=$(grep -o 'sha256.*$' release-test.rb | cut -c9-)
PREV_CHECKSUM=${PREV_CHECKSUM_FROM_FILE%?}
echo ${PREV_NAME}
echo ${PREV_CHECKSUM}
sed -i '.bak' "s/$PREV_NAME/$NAME/g" release-test.rb
sed -i '.bak' "s/$PREV_CHECKSUM/$CHECKSUM/g" release-test.rb
cat release-test.rb
git add .
git commit -m "Update formula version"
git push origin-deploy master
После этого, чтобы скачать и установить к себе на mac готовое приложение, необходимо запустить команду brew install USERNAME/REPO_NAME/TOOL_NAME, где USERNAME — имя на github, REPO_NAME — соответственно репозиторий, а TOOL_NAME — название вашего приложения.
Стоит добавить, что для доступа на github и bintray в .travis.yml надо добавить ваши ключи.
Подведем итог
Теперь по команде mvn release
You must be registered for see links
You must be registered for see links