In this article, I want to describe my experience in delivering Graal Native Image to end users of Mac OS and Linux in one command using Travis CI.
When I came across this problem, I felt a lack of information on this topic, so I decided to describe my experience here.
Let's start.
I have:
- Java application with all configurations to build native image.
What I want to get:
- Build a native image with Travis
- Build rpm and deb distributions, as well as an archive for the mac version
- Pour received packets on Bintray
- Give users the ability to download the application through the package manager
- Automate the process as much as possible.
Go
First, create a simple Hello world.
package release.test;
public class Application {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
And add base plugins to pom.xml.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>release.test.Application</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<tagNameFormat>${project.version}</tagNameFormat>
</configuration>
</plugin>
</plugins>
</build>
Next, I will create two profiles for building mac and linux, respectively. I will use it maven-assembly-plugin
. I will not clog the article with tons of xml, instead I will attach a link with a full example.
Let's move on to Travis CI. This is where I want to build a native image, pack it in the right packages and deploy it.
We will create a file .travis.yml
to describe the deployment plan and we will gradually fill it.
To start, it will look like this:
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 https://github.com/oracle/graal/releases/download/vm-19.1.1/graalvm-ce-darwin-amd64-19.1.1.tar.gz
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 https://github.com/oracle/graal/releases/download/vm-19.1.1/graalvm-ce-linux-amd64-19.1.1.tar.gz
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 <john@doe.com>
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 <john@doe.com>
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. , fpm. 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. . , . 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 https://api.bintray.com/content/aarrsseni/release-test/release-test/$TRAVIS_TAG/release-test-$TRAVIS_TAG.zip?publish=1
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 "https://bintray.com/aarrsseni/release-test/download_file?file_path=release-test-1.70.zip"
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 https://github.com/aarrsseni/homebrew-test-release
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:prepare
native image mac linux, .
homebrew